量子プログラミング入門→量子コンピューティングサービス構築入門

量子コンピューター初心者の筆者が日々学びながら、量子コンピュータ上で量子プログラミングできるようなるまでの学習日記を記録していきます。 内容は量子コンピューター入門者向けで、専門家の方からするとおかしな内容があるかもしれません。その際はコメント等でお知らせください!

December 2019

Qubit解説

Github
https://github.com/microsoft/QuantumKatas/blob/49f4e543167be2875ea4c293cd12771383828462/tutorials/Qubit/Qubit.ipynb


(いきなりこのページに飛んできた人で最初から勉強されたい方はこちらをご参照ください)

数学的前提知識の学習が終わっていよいよ量子っぽい話になりました。
Qubitはexerciseがないので解説だけになります。

古典コンピュータではBitが基本的な構成要素となっている。
量子コンピューターにおいてはqubitが基本的な構成要素となる
bitは0と1の二値を持つことができる。

qubitは観測した際にはZero(|0>)もしくはOne(|1>)のどちらかの値が測定できるが、
実際にはsuperposition(量子重ね合わせ)によりより多くの値を持つことができる。
|0>と|1>は単純なスカラではなくそれぞれ下記のベクトルで表現される
\[ |0> = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \] \[ |1> = \begin{bmatrix} 0 \\ 1 \end{bmatrix} \]
この|0>と|1>の状態をcomputational basis, または the canonical basisと呼ぶ

ZeroとOneが読める確率を表現するために確率振幅αとβを用いる。αをZeroのための係数、βをOneのための係数とし、qubitの状態は下記の表現で表せる
\[ \alpha \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix} + \beta \cdot \begin{bmatrix} 0 \\ 1 \end{bmatrix} = \begin{bmatrix} \alpha \\ 0 \end{bmatrix} + \begin{bmatrix} 0 \\ \beta \end{bmatrix} = \begin{bmatrix} \alpha \\ \beta \end{bmatrix} \] 

このαとβの確率振幅は下記の条件を満たす。
\[|\alpha|^2 + |\beta|^2 = 1\] 

常に|0>が読めるような状態は\[\alpha=1, \beta = 0\] 
常に|1>が読めるような状態は\[\alpha=0, \beta = 1\] 
となる。

なんとなくイメージはわかるが、だからなんなのかよくわからん....
とりあえずそういうものなんだと丸暗記しておく。


Hadamard basis(アダマール基底)というのも先々使いそうなのでとりあえずそういうものだと覚えておく。
\[ \begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} \end{bmatrix} \text{ and } \begin{bmatrix} \frac{1}{\sqrt{2}} \\ -\frac{1}{\sqrt{2}} \end{bmatrix} \]

Dirac Notation(ブラ・ケット記法)
これらのベクトルをより簡易に表現するための記法。
\[ |0\rangle = \begin{bmatrix} 1 \\ 0 \end{bmatrix}  \text{ and }  |1\rangle = \begin{bmatrix} 0 \\ 1 \end{bmatrix} \]

|+>, |->, |i>, |-i>は一般的に下記の意味で用いられる
\[ |+\rangle = \frac{1}{\sqrt{2}}\big(|0\rangle + |1\rangle\big) \] \[ |-\rangle = \frac{1}{\sqrt{2}}\big(|0\rangle - |1\rangle\big) \]
\[ |i\rangle = \frac{1}{\sqrt{2}}\big(|0\rangle + i|1\rangle\big) \] \[ |-i\rangle = \frac{1}{\sqrt{2}}\big(|0\rangle - i|1\rangle\big) \]

Q#でのQubitデータ型

Qubit型

  • Q#でqubitを表すためにQubit型が使われる。
  • 物理的な量子コンピュータではqubitの正確な状態を直接読んだり、直接状態を変更したりすることはできないが、量子ゲートを使用することによりこれらの値を変更したり測定することがことができる。
  • The qubitは通常のデータ型ではないため、使用前に宣言する必要がある
  • 宣言された直後のqubitは|0>であり、そのブロックが終了するまでは|0>であり続ける。

// This statement allocates a qubit, and binds it to the variable q
using (q = Qubit()) {
    // You can work with the qubit here
    // ...
}
// The qubit is no longer allocated outside of the 'using' block

Q#のQubitオペレーション

ここのDemoで使われるオペレーションはこちらで説明されているが、読むだけではよくわからないのでいろいろ動かしてみる。このあたりは古典コンピューターで言うANDとORとかBit Shiftとか基礎中の基礎なのかなと思う。

X ゲート

|0⟩ と |1⟩ の状態を入れ替える
|0>が読める状態なら|1>に、|1>が読める状態なら|0>変換する。
\[ {\sigma_x = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} } \]
        // This line changes the qubit from state |0⟩ to state |1⟩
        X(q);

Hゲート(アダマール ゲート)

与えられた量子ビットに対してアダマール変換をかけます。
アダマールゲートの使い方として、|0>か|1>が100%観測される状態から、0か1の確率が全く等しい量子ビットを生成する。またHゲートを一度適用した状態からもう一度適用すると元の状態に戻すことができる。
\[ {H = \frac{1}{\sqrt 2} \begin{pmatrix} 1&1 \\ 1&-1 \end{pmatrix} } \]
        // This line changes the qubit to state |-⟩ = (1/sqrt(2))(|0⟩ - |1⟩)
        // That is, this puts the qubit into a superposition
        // 1/sqrt(2) is approximately 0.707107
        H(q);

S (ゲート)

π/2ずつ回転させていく、回転というとよくわからないが、|+>に適用すると |->になる。
順に適用していくと
|+> ⇒ |-> ⇒ |i> ⇒ |-i> ⇒ |+>
という順に状態が変化する。(上記の表の時計回りに状態が遷移)
\[ {S = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} } \]
	// This line changes the qubit to state |-i⟩ = (1/sqrt(2))(|0⟩ - i|1⟩)
        S(q);

(執筆時点で)
MSのリファレンスではSゲートは
"Applies the π/4 phase gate to a single qubit."とあるが、 \( i = \ e^{i\pi/2}  \)なので、Sゲートは\( \pi/2 \)ずつ回転の間違い
Tゲートも"Applies the π/8 gate to a single qubit."とあるが \( \pi/8 \)ではなく \( \pi/4 \)の間違いですね。

\( 2 \pi =360\degree \)なので \( \pi/2 \)を4回適用して1周回る感じですね。

ウィキペディア参照

R (ローテーションゲート)

\[ R_x : x軸に与えられた角度を加える。 \] 
\[ R_y : y軸に与えられた角度を加える。 \]
よくわからないが、ブロッホ球の任意の場所にベクトルを向ける際に使うqubit操作なのかなと想像しておく
        // This will put the qubit into an uneven superposition,
        // where the amplitudes of |0⟩ and |1⟩ have different moduli
        Rx(2.0, q);
        Ry(1.0, q);

x,y,zの回転軸の説明はこちらにあります。


icon




これらの関数ももう少し使い込んでみれば、どういうときに有効なのがわかってくると期待している。


チュートリアル Linear Algebra Part3解答例

Linear Algebra=線形代数
Sample Answers for QuantumKatas Linear Algebra part I
https://github.com/microsoft/QuantumKatas/tree/master/tutorials/LinearAlgebra 

(いきなりこのページに飛んできた人で最初から勉強されたい方はこちらをご参照ください)

ここではQ#による量子プログラミングにあたっての数学的前提知識の学習を行います。
前提知識の学習はPart 3がいよいよ最後です。

Eigenvalue: 固有値
Eigenvector: 固有ベクトル

Exercise 13: Finding an eigenvalue

固有値を求める
固有ベクトルはゼロベクトルではないので、2つの行列を掛け算し、結果をVのゼロでない要素で割り算すれば求まります。
@exercise

def find_eigenvalue(a : Matrix, v : Matrix) -> float:

    n = len(v)
    x = matrix_mult(a, v)

    for i in range(n):
        if v[i][0] ==0:
            continue
        else:
            return x[i][0] / v[i][0]


Exercise 14**: Finding an eigenvector

固有ベクトルを求める。固有ベクトルの求め方については下記リンクを参照
https://to-kei.net/linear-algebra/eigenvalue/eigenvalue-and-eigenvector/

行の要素がオールゼロの場合は別の行を選択する必要があります。

@exercise
def find_eigenvector(a : Matrix, x : float) -> Matrix:
    v = create_empty_matrix(2, 1)
    if a[0][0] == 0 and a[1][0] == 0:
        v[0][0] = 0
        v[1][0] = a[1][1] / x
    elif a[1][0] == 0 and a[1][1] == 0:
        v[0][0] = a[0][0] / x
        v[1][0] = 0
    else:
        v[0][0] = a[0][1]
        v[1][0] = x - a[0][0]

    return v



いよいよ次は基本的な数学の準備から抜け出して、量子ビット(Qubit)について学習していきます。






チュートリアル Linear Algebra Part2解答例


Linear Algebra=線形代数
Sample Answers for QuantumKatas Linear Algebra part I
https://github.com/microsoft/QuantumKatas/tree/master/tutorials/LinearAlgebra 

(いきなりこのページに飛んできた人で最初から勉強されたい方はこちらをご参照ください)

ここではQ#による量子プログラミングにあたっての数学的前提知識の学習を行います。
Part 2は内積・外積などを学習します。

Exercise 9: Inner product. (行列の内積)

行列の内積(スカラー積)

@exercise
def inner_prod(v : Matrix, w : Matrix) -> complex:
    n = len(v)
    
    c = create_empty_matrix(n, 0)
    c = conjugate(v)
    
    x = 0 + 0j
    for i in range(n):
        x =x + c[i][0] * w[i][0]

    return(x)


Exercise 10: Normalized vectors

単位ベクトルの生成

@exercise
def normalize(v : Matrix) -> Matrix:
    n = len(v)
    m = len(v[0])
    
    w = inner_prod(v, v)
    a = math.sqrt(w.real)
    c = create_empty_matrix(n, m)

    for i in range(n):
        c[i][0] = v[i][0] / a

    return c


Exercise 11: Outer product

行列の外積(ベクトル積)
mは変数にしなくても1でハードコーディングでよいです。
@exercise
def outer_prod(v : Matrix, w : Matrix) -> Matrix:
    n = len(v)
    m = len(w)    

    c = create_empty_matrix(m, 0)
    c = conjugate(w)

    d = create_empty_matrix(n, m)
    for i in range(n):
        for j in range(m):
            d[i][j] = v[i][0] * c[j][0]
    return(d)

Exercise 12*: Tensor Product.

テンソル積
出来上がる行列のインデックスをきちんと考えればそれほど難しい問題ではないですね。

@exercise
def tensor_product(a : Matrix, b : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])    
    k = len(b)
    l = len(b[0])

    c = create_empty_matrix(n * k, m * l)
    for ia in range(n):
        for ja in range(m):
            for ib in range(k):
                for jb in range(l):
                    c[ia*k+ib][ja*l+jb] = a[ia][ja]*b[ib][jb]
    return c




次はLinear Algebra Part 3です。Go to Part 3



チュートリアル Linear Algebra Part1解答例


Sample Answers for QuantumKatas Linear Algebra part I
https://github.com/microsoft/QuantumKatas/tree/master/tutorials/LinearAlgebra 

(いきなりこのページに飛んできた人で最初から勉強されたい方はこちらをご参照ください)

ここではQ#による量子プログラミングにあたっての数学的前提知識の学習を行います。
2つ目のチュートリアルは線形代数です。(Linear Algebra=線形代数) Part 1からPart 3までそれぞれのPartに分けて解説します。

(この記事を書くにあたっていろいろ調べていて知ったのですが、現在は高校の数学で行列を学ばないんですね。)

Exercise 1: Matrix addition. (行列の足し算)

まずは行列の足し算です。ここは単純に同じ位置(i, j)にある値を足し合わせるだけなので簡単ですね。

@exercise
def matrix_add(a : Matrix, b : Matrix) -> Matrix:
    # You can get the size of a matrix like this:
    n = len(a)
    m = len(a[0])
    
    # You can use the following function to initialize an n×m matrix filled with 0s to store your answer
    c = create_empty_matrix(n, m)
    
    # You can use a for loop to execute its body several times;
    # in this loop variable i will take on each value from 0 to n-1, inclusive
    for i in range(n):
        # Loops can be nested
        for j in range(m):
            # You can access elements of a matrix like this:
            x = a[i][j]
            y = b[i][j]
            
            # You can modify the elements of a matrix like this:
            c[i][j] = x + y
    
    return c



Exercise 2: Scalar multiplication (行列の整数倍)

行列の整数倍です。これも各成分を整数倍するだけなので簡単にできます。

@exercise
def scalar_mult(x : complex, a : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])    
    c = create_empty_matrix(n, m)
    
    for i in range(n):
        for j in range(m):
            c[i][j] = a[i][j] * x
            
    return c

Exercise 3: Matrix multiplication (行列の掛け算)

行列同士の掛け算です。行列同士の掛け算は行x列で各成分を計算していきます。
@exercise
def matrix_mult(a : Matrix, b : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])
    l = len(b[0])

    
    c = create_empty_matrix(n, l)
    
    for i in range(n):
        for j in range(l):
            for k in range(m):
                x = a[i][k]
                y = b[k][j]
                c[i][j] += x * y
    
    return c


Exercise 4: Matrix Inversion.  (逆行列)

Inverse Matricesとは逆行列のことです。逆行列とはかけると単位行列になる行列のことを言います。
単位行列とはその対角成分に 1 が並んで他は全て 0 となる行列です。
例えば、
\[ \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\
0 & 0 & 1 \\
\end{bmatrix} \] のような行列を言います。
\(I_n\)を単位行列とすると、行列\(A\)の逆行列\(A^{-1}\)は下記のようにあらわせます。
\[AA^{-1} = A^{-1}A = I_n\]

ここでは逆行列を生成してみます。
@exercise
def matrix_inverse(a : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])

    c = create_empty_matrix(n, m)
    d = (a[0][0]*a[1][1] - a[0][1]*a[1][0])

    c[0][0] = a[1][1] / d
    c[0][1] = a[0][1] / d * -1
    c[1][0] = a[1][0] / d * -1
    c[1][1] = a[0][0] / d
    
    return c
参考リンク
https://linear-algebra.com/entry/inverse-matrix
https://mathtrain.jp/inversematrix

Exercise 5: Transpose. (転置行列)

Transposeとは転置行列のことです。転置行列とは行列成分を対角線で折り返した行列のことです。言葉で言うと難しいですが、行列の各要素を添え字\(i, j\)で表した場合、\(i, j\)をひっくり返したものが転置行列になります。 \[A = \begin{bmatrix} x_{0,0} & x_{0,1} & \dotsb & x_{0,m-1} \\ x_{1,0} & x_{1,1} & \dotsb & x_{1,m-1} \\ \vdots & \vdots & \ddots & \vdots \\ x_{n-1,0} & x_{n-1,1} & \dotsb & x_{n-1,m-1} \end{bmatrix}\] Aの転置行列\(A^T\)は \[A^T = \begin{bmatrix} x_{0,0} & x_{1,0} & \dotsb & x_{n-1,0} \\ x_{0,1} & x_{1,1} & \dotsb & x_{n-1,1} \\ \vdots & \vdots & \ddots & \vdots \\ x_{0,m-1} & x_{1,m-1} & \dotsb & x_{n-1,m-1} \end{bmatrix}\] となります。
プログラムで書くと配列の要素番号の入れ替えなので非常に簡単ですね。

@exercise
def transpose(a : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])    
    c = create_empty_matrix(m, n)
    
    for i in range(m):
        for j in range(n):
            c[i][j] = a[j][i]
            
    return c

Exercise 6: Conjugate. (共役行列)

Conjugateは複素数でも出てきましたが、共役(きょうやく)行列のことです。
共役行列とは行列A の成分を複素共役にした行列 A です。
複素共役とは複素数\(a+bi\) (\(a, b\)は実数)に対して虚数部をマイナスにした\(a-bi\)を共役複素数でしたね。(詳しくはこちら)
ここでは共役行列を生成します。
@exercise
def conjugate(a : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])
    
    c = create_empty_matrix(n, m)
    
    for i in range(n):
        for j in range(m):
            c[i][j] = a[i][j].real - a[i][j].imag * 1j
    
    return c

Exercise 7: Adjoint. (随伴行列)

Adjointとは随伴行列のことです。随伴行列とは転置行列+共役行列のことで、行列 A に対して、A の転置およびその成分の複素共役をとったものが随伴行列\(A^\dagger\)(† ダガーと読む、Aのダガーはエーダガー)となります。
つまり、\(A^\dagger = \overline{(A^T)} = (\overline{A})^T\)となります。
随伴行列は\((AB)^\dagger = B^\dagger A^\dagger\)のように計算することができます。

ここでは随伴行列を生成してみます。
@exercise
def adjoint(a : Matrix) -> Matrix:
    n = len(a)
    m = len(a[0])

    c = create_empty_matrix(n, m)
    c = transpose(conjugate(a))
    return c

Exercise 8: Unitary Verification (ユニタリー行列)

ユニタリー行列は量子コンピュータにおいて非常に重要です。
ユニタリー行列とは逆行列変換可能でかつその逆行列が随伴行列と一致する行列のことを指します。つまり\(U^{-1} = U^\dagger\)であり\(UU^\dagger = U^\dagger U = I_n\)となります。
下記\(U\)はユニタリー行列の1つの例です。
\[U = \begin{bmatrix} \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ \frac{i}{\sqrt{2}} & \frac{-i}{\sqrt{2}} \\ \end{bmatrix}\] \[U\dagger = \begin{bmatrix} \frac{1}{\sqrt{2}} & \frac{-i}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} & \frac{i}{\sqrt{2}} \\ \end{bmatrix}\] \[UU\dagger = \begin{bmatrix} \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ \frac{i}{\sqrt{2}} & \frac{-i}{\sqrt{2}} \\ \end{bmatrix}\ \begin{bmatrix} \frac{1}{\sqrt{2}} & \frac{-i}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} & \frac{i}{\sqrt{2}} \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ \end{bmatrix}\]

ここでは各行列がユニタリー行列かをチェックするプログラムを作成します。
from pytest import approx

@exercise
def is_matrix_unitary(a : Matrix) -> bool:
    n = len(a)

    c = create_empty_matrix(n, n)
    d = create_empty_matrix(n, n)

    c = adjoint(a)

    d = matrix_mult(a,c)
    for i in range(n):
        for j in range(n):
            if i == j:
                if d[i][j] != approx(1):
                    return False
            else:
                if d[i][j] != approx(0):
                    return False
    return True


次はLinear algebra Part 2です。 Go to Part 2

QuantumKatas チュートリアルComplexArithmetic の解答例

Sample Answers for QuantumKatas ComplexArithmetic tutorial.
https://github.com/microsoft/QuantumKatas/tree/master/tutorials/ComplexArithmetic

(いきなりこのページに飛んできた人で最初から勉強されたい方はこちらをご参照ください)

Microsoft Q#のQuantumKatasのチュートリアルの解説、解答例を紹介します。
初回はComplex Arithmetic(複素数)です。ここではQ#による量子プログラミングにあたっての数学的前提知識の学習を行います。この段階ではQ#は使用せずPythonのみの使用となります。
ここでは量子プログラミングを学ぶための基礎知識の習得であって、数学的にはあまり深く考えずに、そういうものだと理解して進めばよいと思います。日本語でも聞きなれないのに英語になるとさらに聞きなれない単語がたくさん出てくると思いますが、ここではなるべく丁寧に解説していきます。間違い等あればコメントいただけると助かります。

Exercise 1: Powers of  𝑖 

まずは虚数(imaginary number)について学んでいきます。虚数とは二乗すると-1になる値です。
あまり深く考えずに\(i\)をそのように定義すると考えて先に進みましょう。
ここでは虚数\(i\)をn乗するプログラムを作成します。
@exercise
def imaginary_power(n : int) -> int:
    # If n is divisible by 4
    if n % 4 == 0:
        return 1
    else:
        return -1
pythonのmathパッケージには虚数の定義があり、虚数\(i\)がjとして定義されています。使用する際はjの前に係数を必ず置く必要がありますが、jを使うとわざわざ条件分岐しなくても下記でも動かすことができます。(jを1jとして使うところがポイントです)
@exercise
def imaginary_power(n : int) -> int:
    return 1j**n

Exercise 2: Complex addition (複素数の足し算)

Complexとは複素数のことです。ここでは複素数(Complex)の足し算を学びます。複素数の定義はこちらです。簡単に言うと実数(Real Number)と虚数(Imaginary Number)を足し合わせたものを複素数と呼びます。複素数の足し算は単純に実部(Real Pat)と虚部(Imaginary Part)を足せばよいです。
@exercise
def complex_add(x : Complex, y : Complex) -> Complex:
    # You can extract elements from a tuple like this
    a = x[0]
    b = x[1]
    
    c = y[0]
    d = y[1]
    
    # This creates a new variable and stores the real component into it
    real = a + c
    # Replace the ... with code to calculate the imaginary component
    imaginary = b + d
    
    # You can create a tuple like this
    ans = (real, imaginary)
    
    return ans

Exercise 3: Complex multiplication (複素数の掛け算)

次は複素数の掛け算です。こちらも難しくはないです。\(i^2=-1\)であることを考慮すると
\[ (a+bi)(c+di) = (a\times c + b\times d\times i^2) + (b\times c\times i + a\times d\times i) = (ac -bd) + (ad + bc)i \]
となります。
@exercise
def complex_mult(x : Complex, y : Complex) -> Complex:
    # Fill in your own code
    a = x[0]
    b = x[1]
    
    c = y[0]
    d = y[1]
    # This creates a new variable and stores the real component into it
    real = a*c - b*d
    # Replace the ... with code to calculate the imaginary component
    imaginary = a*d + b*c
    
    ans = (real, imaginary)
    return ans

Exercise 4: Complex conjugate (複素共役)

Complex Conjugateとは複素共役 (ふくそきょうやく)のことです。
複素共役とは複素数\(a+bi\) (\(a, b\)は実数)に対して虚数部をマイナスにした\(a-bi\)を共役複素数と言います。
共役複素数の特徴の1つとして、元の複素数にに複素共役をかけることで実数になることをここでは理解します。
\[(a + bi)(a - bi) = a^2 + b^2\] 
@exercise
def conjugate(x : Complex) -> Complex:
    a = x[0]
    b = x[1]
    ans = (a, -b)
    return ans

Exercise 5: Complex division (複素数の割り算)

次に複素数の割り算を理解します。ここで複素共役を使います。
分母の複素数の複素共役を分母分子それぞれにかけると分母が実数になり割り算できます。
\[\frac{x}{y} = \frac{a + bi}{c + di} = \frac{(a + bi)(c - di)}{(c + di)(c - di)} = \frac{(a + bi)(c - di)}{c^2 + d^2} \]
@exercise
def complex_div(x : Complex, y : Complex) -> Complex:
    a = x[0]
    b = x[1]
    
    c = y[0]
    d = y[1]

    r = (a*c + b*d) / (c**2 + d**2)
    i = (b*c - a*d) / (c**2 + d**2)
    ans = (r, i)
    return ans

Exercise 6: Modulus. (複素数の絶対値)

complex_plane

Modulusとは絶対値です。英語でAbsolute Valueと呼ぶことが多いですが、Quantum KatasではModulusと呼んでいます。ここでは複素数の絶対値(大きさ)について学びます。この辺りはベクトルの考え方と同じなのでベクトルを理解していれば問題はないでしょう。a, bそれぞれのの要素を2乗して足し合わせて、その和の平方根(Square Root)をとります。
@exercise
def modulus(x : Complex) -> float:
    a = x[0]
    b = x[1]
    
    ans = (math.sqrt(a**2 + b**2))
    return ans

Exercise 7: Complex exponents (複素数の指数演算)

次に実数の複素数乗について考えます。
ここでは自然対数の底eの複素数乗について学びます。Tutorialでは"Euler's constant"と書いてありますが、オイラーの定数ではなくネイピア数です。(Pythonではmath.eとして呼び出すことができます。)
あまり深く考えずに\(e^{i\theta} = \cos \theta + i\sin \theta\)を適用していきます。
@exercise
def complex_exp(x : Complex) -> Complex:
    a = x[0]
    b = x[1]

    ans = (math.pow(math.e, a) * math.cos(b), math.pow(math.e, a) * math.sin(b)) 
    return ans

Exercise 8*: Complex powers of real numbers (複素数の指数演算)

ここではeを使わずに任意の実数の複素数乗を考えます。rが0の場合だけ例外処理を追加し、その他の場合は\(log(r)\)をとればよいです。
@exercise
def complex_exp_real(r : float, x : Complex) -> Complex:
    a = x[0]
    b = x[1]

    if r==0:
        return (0, 0)
    else:
        c = math.log(r)
    
    ans = (math.pow(math.e, c*a) * math.cos(c*b), math.pow(math.e, c*a) * math.sin(c*b))
    return ans

Exercise 9: Cartesian to polar conversion (極座標系)

直交座標(Cartesian)⇒極座標(polar)変換について学びます。
complex_polar



極座標への変換は\( e^{i\theta} = \cos\theta + i\sin\theta \)を使えば解けます。
\(\theta\)を求めるには逆三角関数Arc Tangentを使います。横軸がReal Part, 縦軸がImaginary Partなので、\(\arctan{\frac{x}{y}}\)で\(\theta\)が求まります。Pythonではmath.atan2関数を使います。math.atanもありますが、aが0になることがあるので場合分けがめんどくさくなります。

@exercise
def polar_convert(x : Complex) -> Polar:
    a = x[0]
    b = x[1]

    theta = math.atan2(b,a)
    r = a / math.cos(theta)
        
    return (r, theta)

Exercise 10: Polar to Cartesian conversion (極座標系)

極座標(polar)⇒直交座標(Cartesian)変換について学びます。
\( e^{i\theta} = \cos\theta + i\sin\theta \)を使えば解けます。
@exercise
def cartesian_convert(x : Polar) -> Complex:
    r = x[0]
    theta = x[1]
    
    ans = (r * math.cos(theta), r * math.sin(theta))
    return ans

Exercise 11: Polar multiplication (極座標系の掛け算)

極座標系の掛け算
またまた\( e^{i\theta} = \cos\theta + i\sin\theta \)を使えば解けます。
極座標を\( (a + bi) (c + di) \)の形に変換すれば、\( (ac -bd) + (ad + bc)i \)となり、実数部と虚数部からそれぞれ解を求められます。
@exercise
def polar_mult(x : Polar, y : Polar) -> Polar:
    r1 = x[0]
    t1 = x[1]
    r2 = y[0]
    t2 = y[1]

    a = r1 * math.cos(t1)
    b = r1 * math.sin(t1)
    c = r2 * math.cos(t2)
    d = r2 * math.sin(t2)
    
    theta = math.atan2((a*d + b*c), (a*c - b*d))
    r = (a*c - b*d) / math.cos(theta)
    return (r, theta)

と思ったら、"Try to avoid converting the numbers into Cartesian form."ですね....  係数は単純な足し算、指数も足し算ですが、\( -\pi < \theta_3 \leq \pi \)の条件に気を付けて答えを返せば大丈夫です。
@exercise
def polar_mult(x : Polar, y : Polar) -> Polar:
    r1 = x[0]
    t1 = x[1]
    r2 = y[0]
    t2 = y[1]

    t3 = (t1 + t2) % (2 * math.pi)
    if t3 > math.pi:
        t3 = t3 - 2*math.pi     
    return ((r1 * r2), t3)

Exercise 12**: Arbitrary complex exponents (任意の複素数の指数演算)

複素数の複素数乗を算出する問題です。

公式

複素数の複素数乗に関しては公式がありますのでこの公式を使います。
複素数\(z\)、実数\( p,q \)について、
\[ \begin{cases} \alpha=p {\rm Log}|z|-q\arg{z}\\ \beta=q {\rm Log}|z|+p\arg{z} \end{cases} \]
として、
\[ z^{p+qi}=e^{\alpha} (\cos{\beta}+i\sin{\beta}) \]
が成り立ちます。ここで\( { \rm Log}(x) \) は実数\( x \)の自然対数を、\( {\rm \arg}(z) \) は複素数\( z \)の偏角(多価関数)をそれぞれ表します。

公式があることに気が付けばすぐに解くことができます。

@exercise
def complex_exp_arbitrary(x : Complex, y : Complex) -> Complex:
    a = x[0]
    b = x[1]
    p = y[0]
    q = y[1]
    
    modulus = (math.sqrt(a**2 + b**2))
    if a == 0 and b == 0:
        return(0, 0)
    theta = math.atan2(b,a)
    alpha = p * math.log(modulus) - q * theta
    beta  = q * math.log(modulus) + p * theta

    real = math.pow(math.e, alpha) * math.cos(beta)
    imag = math.pow(math.e, alpha) * math.sin(beta)

    return(real, imag)



次はLinear Algebra=線形代数の学習です。もう少し数学のお勉強が続きます。


量子コンピュータ・量子プログラミングを学習していると普段聞きなれない英単語がよく出てきます。
こちらにそれらの英単語や表現を順次メモしていきます。


superposition 重ね合わせ
quantum entanglement 量子もつれ
bell states ベル状態
complex number 複素数
real number 実数
imaginary Number 虚数
Matrix Inversion 逆行列の生成
Complex Conjugate 複素共役
modulus 絶対値
Imaginary Exponents 虚数の指数
calculus 微積分
polar coordinates 極座標
Inner product 行列の内積
Normalized vectors 単位行列
Outer product 行列の外積
Eigenvalue 固有値
Eigenvector 固有ベクトル



Microsoft Q# (qsharp)とは


Q# (キューシャープ) は、量子コンピューターのためにマイクロソフトが開発した、プログラミング言語です。古典コンピュータ上のホストプログラムから量子コンピュータ上で実行されるサブルーチンを呼び出す形で使用される量子プログラミング言語です。 Q#は量子シミュレーターが実装されており実際の量子コンピュータを使わなくても量子プログラミングを始めることができます。

Microsoft Quantum Katasとは

Quantum Katasはマイクロソフトが作成した量子コンピューティングとQ#プログラミングを学習するためのチュートリアルです。Github上で問題や解答、Jupyter notebookが公開されており、量子プログラミングに必要な基礎知識からQ#を使ったコーディングまで習得できるように構成されています。
https://github.com/microsoft/QuantumKatas

The Quantum Katas are a series of self-paced tutorials to help you learn quantum computing and Q# programming.

QuantumKatasはTutorialやKatasなどから構成されており、歴史的にはKatasのほうが古いですが、Tutorial(チュートリアル)から始めることで、量子プログラミングに必要な数学的基礎知識から習得することができます。
チュートリアルのメニューは以下です。数学の基礎的な知識から入るのでこれから量子プログラミングを始めるとい自分のような超初心者にもちょうどいい感じです。逆に言うと数学的な基礎知識がないと現時点では量子プログラミングを学ぶのは難しいです。ただこれらの数学は普段は見慣れないのでとっつきにくいところもありますが、論理的にはそれほど難しいものではないので、順を追ってしっかり学習していけば問題ありません。


Quantum Katasを動かすための環境設定

Quantum Katasをonline動かす

一番手っ取り早いのが、こちらのリンクから動かすことです。
ちょっと使いづづけてないとすぐにセッションが切れてしまいますが、環境を自分で用意しなくてよいので非常に便利です。

Quantum Katasを自分のJupyterで動かす

jupyterインストール&起動

Katasのチュートリアルを動かすためにはjupyter notebookが必要です。
自分でJupyterを動かす場合は、こちらを参考にAnacondaをインストール

gitからクローン


自分で動かす場合、下記コマンドでQuantumKatasをJupyterから見えるフォルダにクローンしてきます。
git clone https://github.com/microsoft/QuantumKatas.git


チュートリアルは
QuantumKatas/tutorials
にあります。

スクリーンショット (31)
ではこれから学習を始めていきたいと思います。

11月のMicrosoftのAzure Quantumに続いてAmazonも量子コンピューティングサービスAmazon Braketを発表しましたね。

https://aws.amazon.com/jp/blogs/aws/amazon-braket-get-started-with-quantum-computing/

Amazon Braket – A fully managed service that allows scientists, researchers, and developers to begin experimenting with computers from multiple quantum hardware providers in a single place.

Amazon Braketはフルマネージドな科学者、研究者、開発者が複数の量子コンピューターを使って1か所で実験することができるサービスです。

量子分野では出遅れ感が否めなかったAmazonですが、今回発表ではD-WaveやRigettiといった古参量子コンピューターベンダーと組むことで挽回を狙ってるように見えます。IONQはAzure Quantumに続いてダブル参戦ですね。

IBM, Google, Microsoft, Amazonと現在の主要クラウドサービスベンダー(プラットフォーマー)がそろって次世代のプラットフォームの競争に参戦した感じです。いよいよ量子コンピューティング市場も本格的に熱くなってきました。

基本的な量子処理を動かしてみる

MS QDKのQuickstartsにある「 Quantum basics with Q#」で量子コンピュータの基本的な処理を動かしてみる。今回は、量子コンピューティングの学習というよりもQ#の文法の勉強の要素が強い。

Q# Operationの定義

Q#のOperationは量子サブルーチンで、外部から呼び出し可能な量子操作です。
引数は()内にタプル型で定義され、戻り値はコロンの後に定義します。
下記のソースの例で言うと、
Q#のoperationの引数はタプルで宣言され、"Set"の引数は、Result型のdesiredとQubit型のq1のタプル、戻り値は引数の後ろにコロン(:)を付けて宣言されUnit型です。
Unit型はvoid型みたいなものと考えていいようです。

namespace Quantum.Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

    operation Set(desired : Result, q1 : Qubit) : Unit {
        if (desired != M(q1)) {
            X(q1);
        }
    }
}

Setオペレーション についての解説

Setオペレーションは量子ビットを観測し、期待値と異なれば反転させる量子オペレーションです。(関数と言ってはいけない?量子オペレーション?)
量子ビットは実際には中間的な値を取っているのですが、観測時は0か1にしか見えないもので、反転とは0を1に、1を0にする操作を意味します。

Setの入力はResult型のdesiredです。Result型とは量子ビットを観測した時に読める値のことで、取りうる値はZeroかOneです。Q#で使えるデータの型についてはこちらを参照

M()は量子ビットの値を観測するQ#の量子オペレーションです。
X()は量子ビットを反転させるQ#の量子オペレーションです。
今回はこのM()とX()を動かすことがメインのテーマですが、サンプルコード通りにやらされてる感は満載です...

実際に動かしてみる


先ほどのSet Operationにテストコードを追加し実際に動かしてみる。
テストプログラムは期待値0と1それぞれで1000回 Set Operationを動かして
期待値をきちんと返すかを確認するプログラムです。
Bell.qsというファイル名で下記を作成
namespace Quantum.Bell {                                                                                                    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

    operation Set(desired : Result, q1 : Qubit) : Unit {
            if (desired != M(q1)) {
                    X(q1);
            }
    }

    operation TestBellState(count : Int, initial : Result) : (Int, Int) {
        mutable numOnes = 0;
        using (qubit = Qubit()) {
            for (test in 1..count) {
                 Set(initial, qubit);
                 let res = M(qubit);
                 // Count the number of ones we saw:
                 if (res == One) {
                     set numOnes += 1;
                }
            }
            Set(Zero, qubit);

            // Return number of times we saw a |0> and number of times we saw a |1>
            return (count-numOnes, numOnes);
        }
    }
}
host.pyというファイル名で下記を作成

import qsharp

from qsharp import Result
from Quantum.Bell import TestBellState

initials = (Result.Zero, Result.One)

for i in initials:
    res = TestBellState.simulate(count=1000, initial=i)
    (num_zeros, num_ones) = res
    print(f'Init:{i: <4} 0s={num_zeros: <4} 1s={num_ones: <4}')

下記コマンドを実行し動作確認
$ python host.py
Preparing Q# environment... ..Init:0 0s=1000 1s=0 Init:1 0s=0 1s=1000
初期値が0なら0(期待値)を100%(1000回)観測
初期値が1なら1(期待値)を100%(1000回)観測

コピペなんでちゃんと動きますよね。


Microsoft Quantum Development Kit環境構築

ゲート型量子コンピュータのプログラミング環境としてMicrosoftがプログラミング言語Q#とその開発キットQDK(Quantum Development Kit)を提供しています。 ゲート型のプログラミングの学習環境としてまずはAzure QuantumのQ#/QDK環境を構築したいと思います。

ここではMSのQDKインストール手順に従って環境構築を実施していきます。 https://docs.microsoft.com/en-us/quantum/install-guide/index?view=qsharp-preview#develop-with-jupyter-notebooks

MS QDK環境構築のための必要パッケージ

Q#/QDKを動かすためにはPythonまたはC#などの.Net言語環境が必要です。 自分の場合はC#よりもPythonのほうがなじみがあるのでPython環境を構築していきます。

MS QDKをPythonで動かすためには下記の環境が必要

  • Python 3.6 or later
  • The PIP Python package manager
  • .NET Core SDK 3.0 or later

ベース環境の構築

自分は普段はWindowsで作業するのですがPython等のプログラミングとなると Windowsはちょっと不便なところもあるのでubuntuの環境を用意したい。 Azure上に構築しようかと考えましたが、Windows Subsystem for Linux(WSL)で Ubuntuが一番お手軽に構築できそうなのでWSL上に環境構築しました。

Window Subsystem for Linuxの有効化とUbuntuのインストール

設定から"アプリ"を開く。
スクリーンショット (5)

右側のメニューから"プログラムと機能"をクリック


スクリーンショット (6)

左のメニューから"Windows機能の有効化または無効化を"クリック

スクリーンショット (7)

Windows Subsystem for Linuxにチェックを入れる。
(再起動が必要)
スクリーンショット (8)
スタートメニューから"Microsoft Store"を起動し、"Ubuntu"を検索しインストール。
スクリーンショット (11)



Host Application環境の構築 (Python環境)

ubuntuの起動と更新

スタートメニューからubuntuを起動 端末から下記のコマンドを実行

sudo apt update
sudo apt -yV upgrade

ubuntuの再起動

WSL上のUbuntuではshutdownコマンドが使えません。Ubuntuのアップデートを有効にするためWindows側からWSLのUbuntuを再起動させます。

スタートメニューからWindowsシステムツール-> コマンドプロンプトを右クリック->その他から管理者として実行

管理者として開いたコマンドプロンプトで下記のコマンドを実行

net stop LxssManager
net start LxssManager

Pythonとpipのインストール

git, cmake, wget, p7zip-full のインストール

bash上で下記コマンドを実行

sudo apt -yV install git cmake cmake-curses-gui cmake-gui wget p7zip-full

Anaconnda3のダウンロードとインストール

Anacondaは入れる必要はないが、jupyter notebookが使えた方が便利な場合があると思いインストール

ホストのWindowsからAnaconda(Linux版)を下記からダウンロード https://www.anaconda.com/download

WindowsとWSL上のUbuntuは下記フォルダを共有しています。

  • Windows側: C:\Users\ユーザー名
  • WSL(Ubuntu)側: /mnt/c/ユーザー名/

ダウンロードしたファイルをこの共有フォルダに移動するとWSL側から利用することができます。
共有フォルダに移動し下記コマンドを実行。(ダウンロードする版数によりコマンド名は異なります)

Anaconda3-2019.10-Linux-x86_64.sh

途中いくつか入力が必要なので内容を確認し適切に入力

Do you wish the installer to initialize Anaconda3 by running conda init? [yes|no]

PATH環境変数の更新が入ってるので、それを反映させるためいったん端末を閉じて、再度Ubuntuを起動 Pathが変わる。

user@hostname$ which python
/usr/bin/python
(base) user@hostname:~$ which python
/home/user/anaconda3/bin/python
(base) user@hostname:~$

.NET Core SDKのインストール

下記リンクから.NET Core SDK(Linux版)(Build/Run Appsと書いてあるやつ)を選択 https://dotnet.microsoft.com/download 自分の場合はUbuntu 18.04なので左のメニューから下記リンクにジャンプ https://docs.microsoft.com/ja-jp/dotnet/core/install/linux-package-manager-ubuntu-1804


スクリーンショット (29)


ubuntu上で下記コマンドを実行

wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo add-apt-repository universe
sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-3.0

MS QDKのインストール

qsharpパッケージのインストール

pip install qsharp

iqsharpパッケージのインストール

dotnet tool install -g Microsoft.Quantum.IQSharp

.NET有効化のためいったん端末を閉じて再度新しい端末を開く

MSのページには--userオプション無しで書いてあるがWSL環境のせいか--userを付けないとうまく入らない

dotnet iqsharp install --user

環境構築完了!

TutorialにあるHello Worldサンプルコードを動かしてみる

MSページからのコピペなので中身はよくわからないがとにかく動かしてみる

まずQ#のコードを作成。Hello from quantum worldを表示するSayHello() Operationの定義
Operation.qsというファイル名で下記のコードを作成

namespace HelloWorld
{
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

    operation SayHello() : Result {
        Message("Hello from quantum world!");
        return Zero;
    }
}

SayHello()を呼び出すためのPythonプログラムをhello_world.pyというファイル名で作成

import qsharp
from HelloWorld import SayHello
SayHello.simulate()

下記コマンドを実行(Pathが通るように、Operation.qsとhello_world.pyを同じフォルダに置きそのフォルダで実行する)

$ python hello_world.py
Preparing Q# environment...
....Hello from quantum world!

けっこう時間がかかるがきちんとメッセージが表示される。環境は構築できHello Worldは表示されるところまでは来たのでここからしっかりとQ#/QDKについて学習していきます。

↑このページのトップヘ