一般社団法人日本量子コンピューティング協会様が新たに量子エンジニア(ゲート式)アドバンスコースとして第1回の教材を上げていただいているので、勝手ながら解説してみます。例によってJupyter notebookを https://github.com/sin-gee/ImageOfQuantumBit/ImageOfGrover2.ipynb に置いておきますのでよろしければ試してみてください。
さて、今回対象にする量子回路は教材の30頁を確認いただきたいですが、最初のバリアの左側と、(面積的にはかなりを占める)中央と、拡散行列と記している右側に分かれています。実は基本構造はこちらで解説したものと同じです。
左側、アダマール部分の解説は飛ばして、中央部分のゴチャゴチャしたところから解説します。この部分、エントリーコースの「バイトのシフト問題」の流用です。A,B,Cさんが以下のシフトを希望していて、どう組めばいいでしょう?という問題でした。
朝 | 昼 | 夜 | |
---|---|---|---|
Aさん | 〇 | ||
Bさん | 〇 | 〇 | |
Cさん | 〇 | 〇 |
でいささか不条理とも思える設定ですが、シフトを分け合うことはできません。例えばBさんの希望を叶えるとBさんは昼と夜両方のシフトに入ります。昼はBさん、夜はCさん...ということはできないです。
エントリーコースではZゲートより左までで測定する設定でした。Answerビットが1である場合の、A,B,Cのビットが1であるのものが答えでした。(AさんとBさんがシフトに入ります。)
エントリーコース以降に追加された右側を見ていきます。
Zゲートの意味についてはこちらを見ていただくとして、そこから右側が何をしているかを見ていきます。これは位相キックバックと呼ばれるものです。この動作を見るため、Zゲートの左側CCXと右側CCXの作用を取り出してみてみます。
最初にCCXを作用する前の状態です。一番左がAnswerビットに相当します。数値の横バーは振幅が無いことを示します(つまりAnswerビットは0)。
CCXを作用させます。下位2ビットとも1の場合にAnswerが1になります(右端の状態)。
ここでAnswerにZゲートを作用させます。|1xx>の位相(右の4つ)を反転させますが、振幅があるのは|111>のみですので、これが反転します(マイナスになります)。
再びCCXを作用させます。Answerビットは0となりますが、下位2ビットが両方1の場合(|11>)に位相が反転しています(マイナスになっています)。
これを順繰り行うことで、答えであるAさんとBさんのビットの位相のみを反転させる訳です。
次に右側の拡散行列ですが、こちらとほぼ同じです。ただし、今回は量子ビットが3ビットになったのと、CZではなくCCXが使われていて前後のアダマールを挟まれている点が異なります。CZとH+CX+Hで同じ動きをするのでしょうか?結論から言えば同じです。
まず、2ビットH+CX+H版はこうなります。
import numpy as np
import math
import matplotlib.patches as patches
def vector_to_vector_rotation_angle(v1, v2): #2ベクトルの角度をπ単位で算出
return np.arccos(np.inner(v1,v2)/(np.dot(np.linalg.norm(v1),np.linalg.norm(v2))))
dims = 2**2 # 2量子ビットのため次元数は2^2となる
x = (1/dims**0.5)
one = np.ones((dims,1))
sKet = x*one # |s>を求める
sBra = np.transpose(sKet)# <s|を求める
HxH = np.array([[ 1, 1, 1, 1],
[ 1,-1, 1,-1],
[ 1, 1,-1,-1],
[ 1,-1,-1, 1]])/2
XxX = np.array([[ 0, 0, 0, 1],
[ 0, 0, 1, 0],
[ 0, 1, 0, 0],
[ 1, 0, 0, 0]])
CZ = np.array([[ 1, 0, 0, 0],
[ 0, 1, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0,-1]])
CX = np.array([[ 1, 0, 0, 0],
[ 0, 1, 0, 0],
[ 0, 0, 0, 1],
[ 0, 0, 1, 0]])
IH = np.array([[ 1, 1, 0, 0],
[ 1,-1, 0, 0],
[ 0, 0, 1, 1],
[ 0, 0, 1,-1]])/2**0.5
q00 = np.array([[1],[0],[0],[0]]) # Try any Vector
print('1回目のアダマール後')
G_1 = np.dot(HxH,q00)
print(G_1)
print('1回目のXゲート後')
G_2 = np.dot(XxX,G_1)
print(G_2)
print('CX前のHゲート後')
G_20 = np.dot(IH,G_2)
print(G_20)
print('CXゲート後')
G_3 = np.dot(CX,G_20)
print(G_3)
print('CX後のHゲート後')
G_30 = np.dot(IH,G_3)
print(G_30)
print('2回目のXゲート後')
G_4 = np.dot(XxX,G_30)
print(G_4)
print('2回目のHゲート後')
t00 = np.dot(HxH,G_4)
print('変換後のベクトル')
print(t00)
print('変換前と変換後(に原点で反転させた)のベクトルの間の角度は',round(vector_to_vector_rotation_angle( q00.reshape(-1),-t00.reshape(-1))/np.pi,3),'pi')
print('変換前のベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle( q00.reshape(-1),sKet.reshape(-1))/np.pi,3),'pi')
print('変換後(に原点で反転させた)ベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle(sKet.reshape(-1),-t00.reshape(-1))/np.pi,3),'pi')
次に3ビットH+CCX+H版
import numpy as np
import math
import matplotlib.patches as patches
def vector_to_vector_rotation_angle(v1, v2): #2ベクトル間の角度をπ単位で算出
return np.arccos(np.inner(v1,v2)/(np.dot(np.linalg.norm(v1),np.linalg.norm(v2))))
dims = 2**3 # 3量子ビットのため次元数は2^3となる
x = (1/dims**0.5)
one = np.ones((dims,1))
sKet = x*one # |s>を求める
sBra = np.transpose(sKet)# <s|を求める
HxHxH =np.array([[ 1, 1, 1, 1, 1, 1, 1, 1],
[ 1,-1, 1,-1, 1,-1, 1,-1],
[ 1, 1,-1,-1, 1, 1,-1,-1],
[ 1,-1,-1, 1, 1,-1,-1, 1],
[ 1, 1, 1, 1,-1,-1,-1,-1],
[ 1,-1, 1,-1,-1, 1,-1, 1],
[ 1, 1,-1,-1,-1,-1, 1, 1],
[ 1,-1,-1, 1,-1, 1, 1,-1]
])/2**1.5
XxXxX =np.array([[ 0, 0, 0, 0, 0, 0, 0, 1],
[ 0, 0, 0, 0, 0, 0, 1, 0],
[ 0, 0, 0, 0, 0, 1, 0, 0],
[ 0, 0, 0, 0, 1, 0, 0, 0],
[ 0, 0, 0, 1, 0, 0, 0, 0],
[ 0, 0, 1, 0, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0, 0, 0, 0],
[ 1, 0, 0, 0, 0, 0, 0, 0]
])
CCX = np.array([[ 1, 0, 0, 0, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0, 0, 0, 0],
[ 0, 0, 1, 0, 0, 0, 0, 0],
[ 0, 0, 0, 1, 0, 0, 0, 0],
[ 0, 0, 0, 0, 1, 0, 0, 0],
[ 0, 0, 0, 0, 0, 1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 1],
[ 0, 0, 0, 0, 0, 0, 1, 0]
])
IIH = np.array([[ 1, 1, 0, 0, 0, 0, 0, 0],
[ 1,-1, 0, 0, 0, 0, 0, 0],
[ 0, 0, 1, 1, 0, 0, 0, 0],
[ 0, 0, 1,-1, 0, 0, 0, 0],
[ 0, 0, 0, 0, 1, 1, 0, 0],
[ 0, 0, 0, 0, 1,-1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 1, 1],
[ 0, 0, 0, 0, 0, 0, 1,-1]
])/2**0.5
q000 = np.array([[1],[0],[0],[0],[0],[0],[0],[0]]) # Try any Vector
print('1回目のアダマール後')
G_1 = np.dot(HxHxH,q000)
print(G_1)
print('1回目のXゲート後')
G_2 = np.dot(XxXxX,G_1)
print(G_2)
print('CCX前のHゲート後')
G_20 = np.dot(IIH,G_2)
print(G_20)
print('CCXゲート後')
G_3 = np.dot(CCX,G_20)
print(G_3)
print('CCX後のHゲート後')
G_30 = np.dot(IIH,G_3)
print(G_30)
print('2回目のXゲート後')
G_4 = np.dot(XxXxX,G_30)
print(G_4)
print('2回目のHゲート後')
t000 = np.dot(HxHxH,G_4)
print('変換後のベクトル')
print(t00)
print('変換前と変換後(に原点で反転させた)ベクトルの間の角度は',round(vector_to_vector_rotation_angle(q000.reshape(-1),-t000.reshape(-1))/np.pi,3),'pi')
print('変換前のベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle(q000.reshape(-1), sKet.reshape(-1))/np.pi,3),'pi')
print('変換後(に原点で反転させた)ベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle(sKet.reshape(-1),-t000.reshape(-1))/np.pi,3),'pi')
最後に3ビットCCZ版
import numpy as np
import math
import matplotlib.patches as patches
def vector_to_vector_rotation_angle(v1, v2): #2ベクトル間の角度をπ単位で算出
return np.arccos(np.inner(v1,v2)/(np.dot(np.linalg.norm(v1),np.linalg.norm(v2))))
dims = 2**3 # 3量子ビットのため次元数は2^3となる
x = (1/dims**0.5)
one = np.ones((dims,1))
sKet = x*one # |s>を求める
sBra = np.transpose(sKet)# <s|を求める
HxHxH =np.array([[ 1, 1, 1, 1, 1, 1, 1, 1],
[ 1,-1, 1,-1, 1,-1, 1,-1],
[ 1, 1,-1,-1, 1, 1,-1,-1],
[ 1,-1,-1, 1, 1,-1,-1, 1],
[ 1, 1, 1, 1,-1,-1,-1,-1],
[ 1,-1, 1,-1,-1, 1,-1, 1],
[ 1, 1,-1,-1,-1,-1, 1, 1],
[ 1,-1,-1, 1,-1, 1, 1,-1]
])/2**1.5
XxXxX =np.array([[ 0, 0, 0, 0, 0, 0, 0, 1],
[ 0, 0, 0, 0, 0, 0, 1, 0],
[ 0, 0, 0, 0, 0, 1, 0, 0],
[ 0, 0, 0, 0, 1, 0, 0, 0],
[ 0, 0, 0, 1, 0, 0, 0, 0],
[ 0, 0, 1, 0, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0, 0, 0, 0],
[ 1, 0, 0, 0, 0, 0, 0, 0]
])
CCZ = np.array([[ 1, 0, 0, 0, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0, 0, 0, 0],
[ 0, 0, 1, 0, 0, 0, 0, 0],
[ 0, 0, 0, 1, 0, 0, 0, 0],
[ 0, 0, 0, 0, 1, 0, 0, 0],
[ 0, 0, 0, 0, 0, 1, 0, 0],
[ 0, 0, 0, 0, 0, 0, 1, 0],
[ 0, 0, 0, 0, 0, 0, 0,-1]
])
q000 = np.array([[1],[0],[0],[0],[0],[0],[0],[0]]) # Try any Vector
print('1回目のアダマール後')
G_1 = np.dot(HxHxH,q000)
print(G_1)
print('1回目のXゲート後')
G_2 = np.dot(XxXxX,G_1)
print(G_2)
print('CCXゲート後')
G_3 = np.dot(CCZ,G_2)
print(G_3)
print('2回目のXゲート後')
G_4 = np.dot(XxXxX,G_30)
print(G_4)
print('2回目のHゲート後')
t000 = np.dot(HxHxH,G_4)
print('変換後のベクトル')
print(t00)
print('変換前と変換後(に原点で反転させた)ベクトルの間の角度は',round(vector_to_vector_rotation_angle(q000.reshape(-1),-t000.reshape(-1))/np.pi,3),'pi')
print('変換前のベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle(q000.reshape(-1), sKet.reshape(-1))/np.pi,3),'pi')
print('変換後(に原点で反転させた)ベクトルと|s>との間の角度は', round(vector_to_vector_rotation_angle(sKet.reshape(-1),-t000.reshape(-1))/np.pi,3),'pi')
ぜひ皆さんの手元で確認してみてください。