前回ではQビットの表し方とその観測という操作について書いた。今回はQビットに対する他のいくつかの操作について説明していきたい。
ひとつのQビットに対する操作
まず、後々の説明を簡単にするためにひとつのQビットに対する操作について。
\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}
おさらいになるが、Qビットは以下に示すように$\ket{0}$と$\ket{1}$との重ね合わせで表現できるのだった。
\begin{align}
\ket{0} &= \left(\array{1 \\\ 0}\right) \
\ket{1} = \left(\array{0 \\\ 1}\right) \\\
\ket{\psi} &= \alpha\ket{0} + \beta\ket{1} \\
&|\alpha|^2 + |\beta|^2 = 1 \tag{1} \\
\end{align}
ここでこのQビットの状態に対してなにかしらの操作を考える。
そのためそれぞれの目的に合わせて以下のように演算子を定義する。
###Xゲート(NOTゲート)
X = \left(
\begin{array}{ccc}
0 & 1 \\
1 & 0 \\
\end{array}
\right) \tag{2}
これはビットの反転を行う。
\begin{align}
X\ket{\psi} &= X(\alpha\ket{0} + \beta\ket{1}) \\
&= \left(
\begin{array}{ccc}
0 & 1 \\
1 & 0 \\
\end{array}
\right)\left(\alpha\left(\array{1 \\\ 0}\right) + \beta\left(\array{0 \\\ 1}\right) \right) \\
&=\alpha\left(\array{0 \\\ 1}\right) + \beta\left(\array{1 \\\ 0}\right) \\
&=\alpha\ket{1} + \beta\ket{0}
\end{align}
となり、確かに反転している。
さて、上記の$X$は以下のようにも表せる。
X = \ket{1}\bra{0} + \ket{0}\bra{1}
$\bra{0}$, $\bra{1}$などは初めて出てくる記号でブラと呼ばれる。
\ket{a} = \left(\array{\alpha \\\ \beta}\right)
としたとき、
\bra{a} = \left(\array{\alpha^* \\\ \beta^*}\right) \\
\alpha^*,\beta^*は\alpha, \betaの共役複素数
である。ここで、
\ket{b} = \left(\array{\gamma \\\ \delta}\right)
として$\bra{a}\ket{b}$を定義する。これは真ん中の棒2本をひとつ省略して$\braket{a}{b}$と書く。これはベクトルの内積を意味する。
\braket{a}{b} = (\alpha^*, \beta^*)\left(\array{\gamma \\\ \delta}\right)
= \alpha^*\gamma + \beta^*\delta
さらに、$\ket{a}\bra{b}$は以下のような演算子として定義。
\ket{a}\bra{b} = \left(\array{\alpha \\\ \beta}\right)(\gamma^*, \delta^*)
= \left(
\begin{array}{ccc}
\alpha\gamma^* & \alpha\delta^* \\
\beta\gamma^* & \beta\delta^* \\
\end{array}
\right)
$\ket{0}$と$\ket{1}$について上の定義を当てはめると、
\braket{0}{0} = 1 \\
\braket{0}{1} = 0 \\
\braket{1}{0} = 0 \\
\braket{1}{1} = 1 \\
になる。さて、あらためて$X$の定義を確かめる。
\begin{align}
X &= \ket{1}\bra{0} + \ket{0}\bra{1} \\
&= \left(
\begin{array}{ccc}
0 & 0 \\
1 & 0 \\
\end{array}
\right)
+ \left(
\begin{array}{ccc}
0 & 1 \\
0 & 0 \\
\end{array}
\right) \\
&=\left(
\begin{array}{ccc}
0 & 1 \\
1 & 0 \\
\end{array}
\right)
\end{align}
式(2)の定義と一致する。
さて、$X$$\ket{\psi}$を定義にしたがってもう一度計算してみる。
\begin{align}
X\ket{\psi} &= (\ket{1}\bra{0} + \ket{0}\bra{1})(\alpha\ket{0} + \beta\ket{1}) \\
&= (\ket{1}\bra{0} + \ket{0}\bra{1})\alpha\ket{0} + (\ket{1}\bra{0} + \ket{0}\bra{1})\beta\ket{1} \\
&=\alpha(\ket{1}\braket{0}{0} + \ket{0}\braket{1}{0}) + \beta(\ket{1}\braket{0}{1} + \ket{0}\braket{1}{1}) \\
&=\alpha\ket{1} + \beta\ket{0}
\end{align}
こんな感じで、ベクトルや行列の計算をしなくても済ますことができる。使いこなせるようになったら確かにめちゃくちゃ便利だ。
###Zゲート
Z = \left(
\begin{array}{ccc}
1 & 0 \\
0 & -1 \\
\end{array}
\right)
= i\ket{0}\bra{0} -i\ket{1}\bra{1}
Zゲートは$\ket{1}$の係数だけを反転する。
上記の$X, Y, Z$はパウリ演算子と呼ばれている。
###Yゲート
Y = \left(
\begin{array}{ccc}
0 & -i \\
i & 0 \\
\end{array}
\right)
= i\ket{1}\bra{0} -i\ket{0}\bra{1}
Yゲートはビット反転して一方の係数をマイナスに変更する。
###アダマールゲート
H = \frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 \\
1 & -1 \\
\end{array}
\right)
= \frac{\ket{0} + \ket{1}}{\sqrt{2}}\bra{0} + \frac{\ket{0} - \ket{1}}{\sqrt{2}}\bra{1}
これは量子の重ね合わせを作る際の操作。
###フェーズゲート
$\ket{1}$側だけ$\theta$の回転。
R_\theta = \left(
\begin{array}{ccc}
1 & 0 \\
0 & e^{i\theta} \\
\end{array}
\right)
= \ket{0}\bra{0} + e^{i\theta}\ket{1}\bra{1}
これらのゲートを逐次的にQビットに適用し状態を変化させていくのが量子計算機である。
たとえば、アダマールゲートにより$\ket{0}$は以下のようになる。
\begin{align}
H\ket{0} &= \frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 \\
1 & -1 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
\end{array}
\right)
=\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 \\
1 \\
\end{array}
\right)
= \frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 \\
0 \\
\end{array}
\right)
+
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
0 \\
1 \\
\end{array}
\right) \\
&=\frac{1}{\sqrt{2}}\ket{0} + \frac{1}{\sqrt{2}}\ket{1}
\end{align}
回転ゲート
Qビットの状態は式(1)で表されるので、この条件を満たす式として以下を考える。
\begin{align}
\ket{\psi} &= \cos{\theta/2}\ket{0} + \mathrm{e}^{i\phi}\sin{\theta/2}\ket{1} \\
&= \cos{\theta/2}\ket{0} + (\cos{\phi} + i\sin{\phi})\sin{\theta/2}\ket{1} \\
&0 \leq \theta \leq \pi ,0 \leq \phi \leq 2\pi
\end{align}
$\ket{\psi}$はブロッホ球と呼ばれる単位球面上の一点で表される。ブロッホ球に関しては検索すれば他のサイトで優れた記事がいくつも見つかる。日本語Wikipediaでも解説されている。
このとき、
\begin{align}
x &= \sin{\theta}cos{\phi} \\
y &= \sin{\theta}sin{\phi} \\
z &= \cos{\theta}
\end{align}
これは、つまり量子の状態というのは角度$\theta, \phi$で決まるということ。
たとえば、量子の状態が$\ket{0}$のとき、
\theta = 0, \phi = 0
量子の状態が$\ket{1}$のとき、
\theta = \pi, \phi = 0
量子の状態が$1/\sqrt{2}(\ket{0} + \ket{1})$のとき、
\theta = \pi/2, \phi = 0
これは、$\ket{0}$をアダマールゲートに通した時の状態である。
Z軸まわりの回転
R_z =
\left(
\begin{array}{ccc}
\mathrm{e}^{-i\theta/2} & 0 \\
0 & \mathrm{e}^{i\theta/2} \\
\end{array}
\right)
ただし、ここでの$\theta$はブロッホ球のところで説明したものではなく、座標軸のZ軸まわりの回転角度のこと。
$\theta=\pi$のとき、$\ket{0}$に適用すると、
\left(
\begin{array}{ccc}
-i & 0 \\
0 & i \\
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
-i \\
0 \\
\end{array}
\right)
= -i\ket{0}
同様に$\ket{1}$に適用すると、
\left(
\begin{array}{ccc}
-i & 0 \\
0 & i \\
\end{array}
\right)
\left(
\begin{array}{ccc}
0 \\
1 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
0 \\
i \\
\end{array}
\right)
= i\ket{1}
$\ket{0}$の係数がマイナスに変更されている。これはQビットの位相が変化したことによる。ブロッホ球上のZ軸の上の頂点を$\ket{0}$、下の頂点を$\ket{1}$としたときその角度の差が$\pi$になっているが、それと同様に位相というのはQビットの状態の$\ket{0}$側と$\ket{1}$側との角度の差のことをいう(上図の$\theta$)Qビットの状態は位相と確率振幅で表されるので、位相を操作することにより状態を変化させることができる。差だけを問題にすればいいのでこの例は前に見たZゲートと同じ操作ということになる。同様に、前に見たフェーズゲートも位相を操作するゲートということになる。
X軸まわりの回転
R_x =
\left(
\begin{array}{ccc}
\cos{\theta/2} & -i\sin{\theta/2} \\
-i\sin{\theta/2} & \cos{\theta/2} \\
\end{array}
\right)
ただし、ここでの$\theta$はブロッホ球のところで説明したものではなく、座標軸のX軸まわりの回転角度のこと。
$\theta=\pi$のとき、$\ket{0}$に適用すると、
\left(
\begin{array}{ccc}
0 & -i \\
-i & 0 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
0 \\
-i \\
\end{array}
\right)
= -i\ket{1}
となり反転している。同様に$\ket{1}$に適用すると、
\left(
\begin{array}{ccc}
0 & -i \\
-i & 0 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
0 \\
1 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
-i \\
0 \\
\end{array}
\right)
= -i\ket{0}
となり、こちらも反転している。係数がどちらも$-i$がつくようになるが、これは位相に当たる部分でどちらも等しく$-i$になっていることからこの変化はQビットの状態としては変化なしとしてよい。つまり、これは前に紹介したXゲート(NOTゲート)と同じ。
Y軸まわりの回転
R_y =
\left(
\begin{array}{ccc}
\cos{\theta/2} & -\sin{\theta/2} \\
\sin{\theta/2} & \cos{\theta/2} \\
\end{array}
\right)
ただし、ここでの$\theta$はブロッホ球のところで説明したものではなく、座標軸のY軸まわりの回転角度のこと。
これはビット反転して位相を変更する。
複数のQビットに対する操作
基本的に単一Qビットに対する操作と同じ。ただ、N個のQビットの状態は$2^N$の状態をとることから、操作は単一のQビットに適用するように単純ではない。
話を簡単にするため2つのQビットの状態を考える。
2つのキュービットの状態は以下の基底のテンソル積で表されることは前回で説明した。
\ket{a}\otimes\ket{b}
= \left(
\begin{array}{ccc}
a_0 \\
a_1 \\
\end{array}
\right)
\otimes
\left(
\begin{array}{ccc}
b_0 \\
b_1 \\
\end{array}
\right)
= \left(
\begin{array}{ccc}
a_0b_0 \\
a_0b_1 \\
a_1b_0 \\
a_1b_1 \\
\end{array}
\right) \tag{3}
最右辺は順序付き4次元の縦ベクトル。ここまでは前回の内容をちょっと表し方を変えたもの。
で、これにゲートを適用するのだが、単一Qビットではゲートを表す行列が2階のテンソルだったのでこれを4階にする。
例としてアダマールゲートを考え、一つ目のQビット$a$にだけ適用するゲート$H_a$を考える。
\begin{align}
I &= \left(
\begin{array}{ccc}
1 & 0 \\
0 & 1 \\
\end{array}
\right) \\
H &= \frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 \\
1 & -1 \\
\end{array}
\right) \\
H_a &= I\otimes H =
\left(
\begin{array}{ccc}
1H & 0H \\
0H & 1H \\
\end{array}
\right) \\
&=
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 & 0 & 0 \\
1 & -1 & 0 & 0\\
0 & 0 & 1 & 1\\
0 & 0 & 1 & -1
\end{array}
\right) \\
\end{align}
次に二つ目のQビット$b$にだけ適用するゲート$H_b$を考える。
H_b = H\otimes I =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 0 & 1 & 0 \\
0 & 1 & 0 & 1\\
1 & 0 & -1 & 0\\
0 & 1 & 0 & -1
\end{array}
\right) \\
いま、式(3)の状態が具体的に以下のような状態にあるとき、
\ket{00} =
\left(
\begin{array}{ccc}
1 \\
0 \\
0 \\
0 \\
\end{array}
\right)
$H_a, H_b$をこの状態に適用してみる。
H_a \ket{00} =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 & 0 & 0 \\
1 & -1 & 0 & 0\\
0 & 0 & 1 & 1\\
0 & 0 & 1 & -1
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
0 \\
0 \\
\end{array}
\right) =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 \\
1 \\
0 \\
0 \\
\end{array}
\right) \tag{5}\\
H_b \ket{00} =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 0 & 1 & 0 \\
0 & 1 & 0 & 1\\
1 & 0 & -1 & 0\\
0 & 1 & 0 & -1
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
0 \\
0 \\
\end{array}
\right) =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 \\
0 \\
1 \\
0 \\
\end{array}
\right) \tag{6}\\
$H_a$を適用し、次に$H_b$を適用すると、
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 0 & 1 & 0 \\
0 & 1 & 0 & 1\\
1 & 0 & -1 & 0\\
0 & 1 & 0 & -1
\end{array}
\right)
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 \\
1 \\
0 \\
0 \\
\end{array}
\right) =
\frac{1}{2}\left(
\begin{array}{ccc}
1 \\
1 \\
1 \\
1 \\
\end{array}
\right) \tag{7}\\
これは以下の演算と同じ。
H^{\otimes2}\ket{00} =
\frac{1}{2}\left(
\begin{array}{ccc}
1 & 1 & 1 & 1 \\
1 & -1 & 1 & -1\\
1 & 1 & -1 & -1\\
1 & -1 & -1 & 1
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
0 \\
0 \\
0 \\
\end{array}
\right) =
\frac{1}{2}\left(
\begin{array}{ccc}
1 \\
1 \\
1 \\
1 \\
\end{array}
\right)
ここで、ちょっと頭を切り替えて前回の記事のことを思い出すと、複数Qビットの状態におけるそれぞれのQビットの観測では、$n$番目のQビットに関して、$2^{n-1}$だけ先の状態とがペアになるという話だった。
$H_a$や$H_b$を見るとまさにそれを表す行列となっており、観測の時に見たようにそれぞれの状態に選択的に演算が適用されるような行列になっている。
これは、Qビットが3つ以上になった場合も同様に計算できる。たとえば3つのキュービット$a, b, c$の8次元ベクトル(3階のテンソル)で表される状態にそれぞれ適用する8階テンソルのアダマールゲート$H_a, H_b, H_c$は以下のようになる。
H_a = I \otimes I \otimes H \\
H_b = I \otimes H \otimes I \\
H_c = H \otimes I \otimes I \\
このようにして、アダマールゲートだけでなく、最初に示した他のすべてのゲートも同様に計算する。
さて、教科書的な説明としてはこんな感じでベクトルや行列のテンソルで説明するのが一般的だと思われるが、以下はシミュレーターを作成する観点で注意すべき点を中心に話してみる。
シミュレーター内での計算方法
シミュレーターでの実際の計算の際には、プログラム内でなにかしらの数値計算ライブラリーを使ってベクトルや行列操作をすることになると思うが、たいていのプログラミング言語には便利な数値計算ライブラリーが組み込まれている。たとえば4つのQビットに対するアダマールゲートとその操作はPythonでは以下のように簡単に記述できる。(Qビットが4つ、ゲートは16階のテンソルの場合で、行列を作る際にクロネッカー積を使っている)
import numpy as np
# 3つのキュービットの初期状態(16次元のベクトル)
a = [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
sqrt = 1/np.sqrt(2)
i = np.array([[1,0,],[0,1]]) # 基底
h = np.array([[sqrt, sqrt],[sqrt, -sqrt]]) # アダマール変換
u1 = np.kron(i, np.kron(i, np.kron(i, h))) # 1つ目のQビットにだけ作用するゲート
u2 = np.kron(i, np.kron(i, np.kron(h, i))) # 2つ目のQビットにだけ作用するゲート
u3 = np.kron(i, np.kron(h, np.kron(i, i))) # 3つ目のQビットにだけ作用するゲート
u4 = np.kron(h, np.kron(i, np.kron(i, i))) # 4つ目のQビットにだけ作用するゲート
q1 = np.dot(u1, a) # 1つ目のQビットにゲートを適用
q2 = np.dot(u2, a) # 2つ目のQビットにゲートを適用
q3 = np.dot(u3, a) # 3つ目のQビットにゲートを適用
q4 = np.dot(u4, a) # 4つ目のQビットにゲートを適用
r1 = np.dot(u2, q1)
r2 = np.dot(u3, r1)
r3 = np.dot(u4, r2) # 全部のQビットに適用した場合の状態
実装としては間違いではないのだが、少し考えるとシミュレーターではこれは非常にまずい実装だということに気づく。ベクトルと行列を用いて上記のコード例のように素直に実装した場合に問題になるのが、ベクトルや行列の次元数だ。前回の記事でも言及したが、Qビットの状態と言うのは$N$個のQビットの場合$2^N$の状態となり、これを従来の計算機上で考えなくてはいけない。$N$が大きくなるにしたがって指数関数的にベクトルや行列は大きくなる。すぐにメモリーやCPUなどのハードウエアの限界が問題になってしまう。せいぜい数個のQビットはシミュレーションできたとしても、数十前半ですでに手元のコンピューターのCPUファンが唸り出すだろう。GPUを利用したとしても、メモリーや計算能力には限界がある(従来の計算機にはない量子的特徴を生かした計算機をシュミレートしようとするのだから当然ではある)
幸い、巨大なベクトルや行列の演算をせずとももっと少ないメモリーとステップで計算する方法がある。ここで、これまでひつこく言及してきた状態ペアを使うのである。状態ペアの数は$2^N/2 = 2^{N-1}$個しかないのだから、選択的にこれらのペアに対して行列演算を適用していけばいいだけだ。例のごとく具体例で見ていく。
3つのQビットつまり、各要素順序付きの8次元の複素ベクトルで表現される状態にアダマールゲートを適用する。
\ket{a} = (a_0,a_1,a_2,a_3,a_4,a_5,a_6,a_7)^T
ここで、二つ目のQビットにゲートを適用する場合の状態ペアは、
(a_0, a_2), (a_1, a_3), (a_4, a_6), (a_5, a_7)
となる。( )内がペアで、それぞれ左側が$\ket{0}$、右側が$\ket{1}$に相当する。なので、それぞれに演算を適用すると以下のようになる。
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
1 & 1 \\
1 & -1 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
a_0 \\
a_2 \\
\end{array}
\right) =
\frac{1}{\sqrt{2}}\left(
\begin{array}{ccc}
a_0 + a_2 \\
a_0 - a_2 \\
\end{array}
\right)
もともと$a_0$だった値が$1/\sqrt{2}(a_0 + a_2)$となり、$a_0$の要素がある位置(左から数えて1番目)が書き換わる。
$a_2$だった値が$1/\sqrt{2}(a_0 - a_2)$となり、$a_2$の要素がある位置(左から数えて3番目)が書き換わる。
同様に他のペアも計算され、8次元ベクトル$\ket{a}$のそれぞれの要素が変更されたため状態が更新される。これは二つ目のQビットに適用した例だが、一つ目、三つ目のQビットにも適切な状態ペアをつくることにより同様のやり方で適用できる。
さて、これまでQビットという名前にビットがついているものの、計算機らしい内容がぜんぜん出てきていない。次回はQビットを従来の計算機のビット同じように扱えることの説明を試みたい。