はじめに
先の回では「量子もつれ」という現象を紹介し、blueqatという名前のフレームワークを用いて量子もつれをシミュレートした。今回は、離散フーリエ変換を量子コンピュータ(量子回路)で行うアルゴリズム「量子フーリエ変換」を紹介し、先と同じようにblueqatでシミュレートする。
離散フーリエ変換
(1)
(2)
を定義すると、式(1)は
(3)
と書くことができる。を成分に持つ行列を導入すると、式(3)は
(4)
と書ける。ここで、である。行列はユニタリー行列になっていることに注意する。すなわち
(5)
が成り立つ。ここで、はのエルミート共役(転置して複素共役をとる)を表す。は単位行列である。ユニタリー性を満たさない演算は量子回路上に載せることができないので、離散フーリエ変換のユニタリー性(式(5))は重要である。
振幅エンコーディング
やのようなベクトルをどのように量子コンピュータに載せるのかを考える。これまでのブログ(1,2)で1量子ビットと2量子ビットを扱った。任意の量子状態は、1量子ビットの場合はの2つの基底状態の重ね合わせで、2量子ビットの場合はの4つの基底状態の重ね合わせで表現されることを見た。ここで、0や1の並びは0以上の整数の2進数表記に相当し、2量子ビットの場合の00,01,10,11は、それぞれ、10進数表記で0,1,2,3に相当する。いま、4次元の単位ベクトルが与えられたとき、各成分を係数に持つ次の量子状態を考える。
(6)
は単位ベクトルとしたから量子状態が満たすべき条件(規格化条件:)は満たされている。つまり、4次元ベクトルは2量子ビットを用いて上のように表すことができる。これをビットに拡張する。
(7)
ただし、である。式(7)は次元ベクトルを表す量子状態である。なら
(8)
となり、8()次元ベクトルを表すことができる。このようにベクトルの成分を量子状態の係数(確率振幅)に埋め込むことを振幅エンコーディングと呼ぶ。どのようにしてこの状態を作り出すのかという話は少々複雑なので今回は言及しない。
量子フーリエ変換
(9)
ここに式(3)を代入すると
(10)
(11)
であった。この2式を見比べると、からに変換するには次の変換を行えばよいことが分かる。
(12)
(13)
(14)
となる。変換を量子フーリエ変換(Quantum Fourier Transform:QFT)と呼ぶ。上式を見ると、の結果からフーリエ係数を求めるにはの係数を取り出す必要があることに注意しなければならない。これを行う手間についてはまた後で触れることにする。
QFTアルゴリズム(ステップ1)
QFTを量子回路に載せる手順(QFTアルゴリズム)を説明する。まず最初に、QFTの式を量子回路に載せやすい形に変形する必要がある。ステップ1ではその変形の過程を示す。古典回路と同様に量子回路の場合も2進数の言葉で書き直す必要がある。
(15)
となる。ところで、のビット2進数表記がとなるとき(例えば、3の2進数表記は11なので)、次式が成り立つ。
(16)
(17)
となる。表記を簡単にするため直積記号は省略した。さらにについての和は次のように書き直すことができる。
(18)
以上のことを考慮すると式(15)は
(19)
となる。ここで、乗積を展開するときはの項が一番左側に来ると約束する。についても2進数表記を行うと
(20)
(21)
となる。右辺の先頭の個の和は正の整数である。これを整数に置き換え、式(21)を指数関数の肩に載せると
(22)
(23)
(24)
を得る。以上の結果をまとめると、式(19)は最終的に次のように変形される。
(25)
(26)
となる。乗積を展開するときはの項が一番左側に来るように配置する。あとはこの式を量子回路で表現すれば良い。その手順を次に示す。
QFTアルゴリズム(ステップ2)
いきなり量子ビットの場合を考えると難しいので、最初に1量子ビット、2量子ビットの場合を考える。
QFTアルゴリズム:1量子ビットの場合
(27)
(28)
(29)
を得る。ここで、を用いた。2進数は10進数で表すとであることに注意する。式(29)は先のブログで紹介したアダマールゲートで実現できる変換である。すなわち、である。の場合のQFTを実現する量子回路は下図である。
アダマールゲートの行列表記は
(30)
である。をとに作用させると式(29)が得られることを確認してほしい。
QFTアルゴリズム:2量子ビットの場合
天下り的であるが先に2量子ビットのQFTを実現する量子回路を示す。
(31)
である。先頭の量子ビットにアダマールゲートをかけると、の場合のQFTの式を参考にして
(32)
を得る。次にのときだけ先頭の状態に位相ゲートを作用させる(このとき量子ビットは制御ビットと呼ばれる)。は次式で定義される行列である。
(33)
この変換を丁寧に見ていく。のときは何もしないので状態はのままである。のときは、量子ビットにを作用させるので
(34)
(35)
と変化することになる。この状態の後ろの量子ビットにアダマールゲートをかけると
(36)
最後に、2つの量子ビットの順番を交換するSWAPゲートを作用させる(今回はSWAPゲートの詳細は省略する)。
(37)
最終的に得られた状態は式(26)でとしたものである。すなわち
(38)
である。
QFTアルゴリズム:量子ビットの場合
の量子回路を拡張しての量子回路を得る。
さらに、拡張すれば、一般のの場合の量子回路を得る。
(39)
である。の場合、SWAPゲートによりビット列は逆順に並べ替えられる。以上で量子フーリエ変換を量子回路で表現できた。
古典アルゴリズム(FFT)との比較
ここでは、QFTアルゴリズムと古典アルゴリズム(FFT)の計算量の見積もりを行う。まず最初にQFTアルゴリズムから考える。量子ゲートであるとの個数の和は、回路内の横線上にある四角の数を数えればよいから
(40)
である。図ので示したSWAPゲートについては今回はその詳細を説明しないが、個のCNOTゲートから構成される(CNOTゲートは先のブログで紹介した)。従って、QFTアルゴリズムで使う総ゲート数は
(41)
となる。これはが大きくなるとのオーダーで大きくなる量である。フーリエ係数の個数に換算するととなる。これが、QFTアルゴリズムの計算量である。一方、FFTの計算量は、である。下図に見る通り、QFTの方が高速であることが分かる。
式(14)の後の文章で触れたとおり、QFTの場合、計算後に個のフーリエ係数を取り出す必要がある。この作業は指数関数的な時間を必要とするため、単にフーリエ係数を知りたいだけならFFTの方が速いというオチが付く。
blueqatによる実装
今回作成したソースコードはここにある。まず最初に2量子ビットの場合のQFTを考える。このときの量子回路は以下であった。
初期状態がの場合のQFTを実現するコードは以下の通り(main_2.py)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def qft_with_2qbit_00(): # |j0j1>=|00> c = Circuit(2) # j0ビットにアダマールゲートをかける。 c = c.h[0] # j1ビットを制御ビットとしてj0ビットにR1をかける。 c = c.cphase(math.pi / 2)[1, 0] # j1ビットにアダマールゲートをかける。 c = c.h[1] # 実行。 r = c.run() print("> 2qbit00") for i, v in enumerate(r): b = "{:02b}".format(i) print("[{}]{}".format(b, v)) |
コード内のコメントで示した通り、量子ゲートをひとつひとつ再現しているだけである。ただし、SWAPゲートは省略した。出力は以下の通り。
1 2 3 4 5 |
> 2qbit00 [00](0.4999999999999999+0j) [01](0.4999999999999999+0j) [10](0.4999999999999999+0j) [11](0.4999999999999999+0j) |
各基底状態()の係数(複素振幅)が出力される。これらは複素数である(Pythonでは虚数単位はjと表記される)。上の回路図に従って手計算を行うとすべての係数が0.5となるので(試してほしい)上の結果と一致している。
次のコードは同じ2量子ビットのQFTであるが、初期状態がの場合である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
def qft_with_2qbit_11(): # |j0j1>=|00> c = Circuit(2) # 両ビットにXゲートをかける。Xゲートはビットを反転させる計算である。状態は|11>になる。 c = c.x[0, 1] # j0ビットにアダマールゲートをかける。 c = c.h[0] # j1ビットを制御ビットとしてj0ビットにR1をかける。 c = c.cphase(math.pi / 2)[1, 0] # j1ビットにアダマールゲートをかける。 c = c.h[1] # 実行。 r = c.run() print("> 2qbit11") for i, v in enumerate(r): b = "{:02b}".format(i) print("[{}]{}".format(b, v)) |
6行目でとの両ビットにゲートを作用させている。ゲートはビットを反転させる量子ゲートである。
(42)
従って、6行目で状態はからに変換される。以降の処理は先のコードと同じである。出力は以下の通り。
1 2 3 4 5 |
> 2qbit11 [00](0.4999999999999999+0j) [01](-3.0616169978683824e-17-0.4999999999999999j) [10](-0.4999999999999999+0j) [11](3.0616169978683824e-17+0.4999999999999999j) |
ここまでのコードでは、までの計算しかしていない。フーリエ係数まで求めるコードは以下の通りである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
def qft(x): ll = len(x) lb = format(ll - 1, 'b') b = len(lb) # 状態|0>に演算子Fを作用させる。 F = Circuit() # 初期状態は|0> for i in range(b): F.h[i] for j in range(1, b - i): F.cphase(math.pi / 2**(j))[i + j, i] w = [] for j in range(ll): c = Circuit() b = format(j, 'b') bl = len(b) # Fが作用する状態|j>を作る。 for a in range(bl): if b[bl - a - 1] == '1': c = c.x[len(lb) - 1 - a] # F|j>を計算する。 # |j>を作る回路cの後ろにフーリエ変換する回路Fを結合する。 d = c + F e = d.run() # w_k,k=0,1,... w.append(e) y = [] for k in range(ll): y_k = 0 for j in range(ll): y_k += w[j][k] * x[j] y.append(y_k) return y |
このコードは一般の量子ビットの場合のQFTである。入力がx
()、出力がy
()である。ここで、である。7行目から11行目までのコードで量子回路F
を構築している。14行目から28行目までのコードでの取り得るすべての状態c
を再現し、その各々をF
に接続している(26行目:d=c+F
)。31行目から35行目までの計算でフーリエ係数y_k
を計算している。量子ビットの場合の量子回路を再掲しておく。
上で実装した関数qft
を用いて次式を離散フーリエ変換してみる。
(43)
(44)
(45)
となる。この結果をqft
で用いて再現してみる。
1 2 3 4 5 6 7 8 9 |
def fun(j): return 5.0 * np.sin(2.0 * 2.0 * np.pi * j / N) def normal_ft(k, xs): s = 0.0 for j in range(N): s += xs[j] * np.exp(complex(0, 2.0 * np.pi * k * j / N)) return s / np.sqrt(N) |
1行目の関数は式(44)を、5行目の関数は式(45)を表す。これらの式を用いて計算したフーリエ係数と、上で実装したqft
による結果とを比較する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
if __name__ == "__main__": xs = [] for i in range(N): xs.append(fun(i)) print("> Classical Fourier Transformation") for k in range(N): y = normal_ft(k, xs) v = np.abs(y) b = "{:04b}".format(k) print("[{}]{}".format(b, v)) print() print("> Quantume Fourier Transformation") ys = qft(xs) for (i, y) in enumerate(ys): v = np.abs(y) b = "{:04b}".format(i) print("[{}]{}".format(b, v)) |
ここでは、フーリエ係数の大きさ()を出力している。結果は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
> Classical Fourier Transformation [0000]1.1102230246251565e-15 [0001]1.5895974606912448e-15 [0010]10.000000000000004 [0011]1.1430445635548515e-15 [0100]2.180278542193117e-15 [0101]2.835419185914153e-15 [0110]4.986125195590856e-15 [0111]7.795513010265399e-15 [1000]2.90236210530186e-15 [1001]8.799524318285616e-15 [1010]3.1401849173675502e-15 [1011]4.718447854656915e-15 [1100]8.253197966801011e-15 [1101]1.7997002884907162e-14 [1110]10.000000000000002 [1111]1.3011534395271244e-14 > Quantume Fourier Transformation [0000]7.771561172376096e-16 [0001]1.7554167342883505e-15 [0010]10.0 [0011]1.4936523181711914e-15 [0100]5.992897688303891e-16 [0101]1.217454592664719e-15 [0110]1.8209011484034673e-15 [0111]1.710067842969272e-15 [1000]1.2212453270876722e-15 [1001]1.807312143953211e-15 [1010]2.144196810752504e-15 [1011]1.0990647210786425e-15 [1100]3.7300332558822236e-16 [1101]2.159233940399377e-15 [1110]9.999999999999998 [1111]1.738660300550457e-15 |
どちらの結果も、0010(=2)番目と1110(=14)番目の項が10、その他の項は0となる。とだけが値を持つのは元の式が周波数2の正弦波のためである(はの複素共役である)。
まとめ
今回は、量子コンピュータを用いた計算の具体例としてフーリエ変換を取り上げ、かなり詳しく解説した(かなり長い記事になってしまった)。量子コンピュータで計算するとは、アルゴリズムを量子回路で表現することである。次回は、回帰を量子コンピュータで計算する方法を説明する予定である。