機会があり、チュートリアルをやってみました。
https://qiskit.org/textbook/ch-algorithms/grover.html
Groverのアルゴリズムは汎用量子アルゴリズムでハイブリッドではないタイプのものです。欲しいデータを探すために整理されていないデータでの検索での利用が想定されています。
1、初期の準備
2、マーキング
3、振幅増幅
の3ステップでできます。1と3は決まった形の回路になっているので、私たちが設計するのは2のマーキング回路で、オラクルとも呼ばれます。
インストール
Qiskitをpythonで利用するには、pip経由でインストールします。量子回路の表記を豊かにするためにおまけも一緒にインストールします。
pip install qiskit pylatexenc
ツール読み込み
ツールを読み込みます。qiskitとnumpyがメインです。
import numpy as np
from qiskit import *
%matplotlib inline
0、回路の初期化
始める前に使う量子ビットを準備します。今回は2量子ビット利用します。
#2量子ビットの回路を作成する
grover = QuantumCircuit(2)
1、初期の準備
Groverでは初期状態は重ね合わせで準備します。これは理論的な理由でそうなっています。別に他の状態を準備してもいいと思いますが、これが一番簡単です。回路は全ての量子ビットにアダマールゲートと呼ばれる重ね合わせの演算を適用します。
#初期状態を作る
grover.h([0,1])
hゲートを適用しました。一旦回路を確認してみます。下記で描画できます。
#量子回路を描画
grover.draw('mpl')
初期状態は00,01,10,11の重ね合わせからスタートします。
2、マーキング
マーキング回路は今回は11のデータを探すものとして作ってみます。状態ベクトルの具体的な説明は今回は省きますが、11のデータは状態ベクトルでは、00,01,10,11の最後の要素になります。最後の要素を見つけるには、CZゲートを使うとマーキングできます。CZゲートは[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1]]のように、状態ベクトルの最後の要素にマイナスをつける行列になっています。
#マーキング回路を作成
grover.cz(0,1)
早速作った回路を確認してみます。
#量子回路を描画
grover.draw('mpl')
3、振幅増幅
こちらは定型作業です。マーキングを増幅させます。対角成分がマイナスの行列が作れればOKです。今回は下記を使いました。こちらのHH-ZZ-CZ-HHの仕組みについては、記事の最後におまけで計算を書いてみます。
#振幅増幅回路を作成
grover.h([0,1])
grover.z([0,1])
grover.cz(0,1)
grover.h([0,1])
grover.draw('mpl')
これで完成です!あとは測定と言って、計算結果を取り出す回路を最後につけます!
4、測定
測定回路をつけます。
# 量子回路に測定を入れる
meas = QuantumCircuit(2, 2)
meas.measure(range(2), range(2))
# 測定込みの回路にする
qc = grover + meas
#量子回路を描画
qc.draw('mpl')
5、計算
早速計算してみましょう。 今回はシミュレータを準備します。実機でgroverを計算した記事もありますので、別途参照してください。shotsと言って1024回同じ計算をします。今回はほぼ無意味ですが、、、そしてジョブを実行して、計算結果を取り出し、カウントします。
#シミュレータを準備
backend_sim = Aer.get_backend('qasm_simulator')
#計算を1024回行う
job_sim = execute(qc, backend_sim, shots=1024)
#計算結果を取り出す
result_sim = job_sim.result()
#計算結果をカウントする
counts = result_sim.get_counts(qc)
print(counts)
予定通り11が1024回出ました。
{'11': 1024}
ヒストグラムの作り方は、
#ヒストグラムへ落とし込む
from qiskit.visualization import plot_histogram
plot_histogram(counts)
こちらの方が見やすいです。オラクルは難しいものを作ればいろんな問題が解けますのでぜひ色々やってみてください。
おまけ
途中の振幅増幅回路のHH-ZZ-CZ-HHがなぜこれなのか、こちらで確認します。マーキングは特定の状態ベクトルだけの符号を反転させます。その後に対角成分が全てマイナスの行列をかけるとマーキングした状態ベクトルの要素だけが増幅します。
import numpy as np
#アダマールゲートを作る
h = np.array([[1,1],[1,-1]])/np.sqrt(2)
h
#array([[ 0.70710678, 0.70710678],
[ 0.70710678, -0.70710678]])
#アダマールゲートのテンソル積をとります
hh = np.kron(h,h)
hh
#array([[ 0.5, 0.5, 0.5, 0.5],
[ 0.5, -0.5, 0.5, -0.5],
[ 0.5, 0.5, -0.5, -0.5],
[ 0.5, -0.5, -0.5, 0.5]])
#Zゲートを作る
z = np.array([[1,0],[0,-1]])
z
#array([[ 1, 0],
[ 0, -1]])
#Zゲートのテンソル積
zz = np.kron(z,z)
zz
#array([[ 1, 0, 0, 0],
[ 0, -1, 0, 0],
[ 0, 0, -1, 0],
[ 0, 0, 0, 1]])
#CZゲート
cz = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1]])
cz
#array([[ 1, 0, 0, 0],
[ 0, 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, -1]])
#振幅増幅回路の行列を確認
hh@zz@cz@hh
#array([[-0.5, 0.5, 0.5, 0.5],
[ 0.5, -0.5, 0.5, 0.5],
[ 0.5, 0.5, -0.5, 0.5],
[ 0.5, 0.5, 0.5, -0.5]])
これで、対角成分が全てマイナスになっているのが確認できました。以上です!では、量子と共に!