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

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

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=線形代数の学習です。もう少し数学のお勉強が続きます。


↑このページのトップヘ