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} \]
\[\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. (複素数の絶対値)

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)変換について学びます。
極座標への変換は\( 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 \)の偏角(多価関数)をそれぞれ表します。
公式があることに気が付けばすぐに解くことができます。
公式
複素数の複素数乗に関しては公式がありますのでこの公式を使います。複素数\(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=線形代数の学習です。もう少し数学のお勉強が続きます。