はじめに
量子コンピュータの演算を行うためには、ゲートを使います。量子ゲートと呼ばれる演算が基本になりますが、この一覧を見て見ます。
ゲートセット
早速見て見ます。
"i": gate.IGate,
"x": gate.XGate,
"y": gate.YGate,
"z": gate.ZGate,
"h": gate.HGate,
"t": gate.TGate,
"s": gate.SGate,
"cz": gate.CZGate,
"cx": gate.CXGate,
"cnot": gate.CXGate,
"rx": gate.RXGate,
"ry": gate.RYGate,
"rz": gate.RZGate,
"phase": gate.RZGate,
"crx": gate.CRXGate,
"cry": gate.CRYGate,
"crz": gate.CRZGate,
"cphase": gate.CU1Gate,
"u1": gate.U1Gate,
"u2": gate.U2Gate,
"u3": gate.U3Gate,
"cu1": gate.CU1Gate,
"cu2": gate.CU2Gate,
"cu3": gate.CU3Gate,
"swap": gate.SwapGate,
"ccx": gate.ToffoliGate,
"toffoli": gate.ToffoliGate,
"t": gate.TGate,
"tdg": gate.TDagGate,
"s": gate.SGate,
"sdg": gate.SDagGate,
"measure": gate.Measurement,
"m": gate.Measurement,
これをみる感じでは、使いやすそうです。使い方を見て見ます。
回路を作る
回路は簡単にできます。
from blueqat import Circuit
Circuit().h[0].m[:].run(shots=1)
Counter({'1': 1})
のようにすることで、
Counter({'1': 1})
答えが戻ってきます。基本的には、
Circuit()
で回路を作り、そこにゲートを適用しますが、
.h[0]
このようにチェーンでつなぎ、[]にゲートを適用したい量子ビットの通し番号を入れます。番号は0からスタートします。一つづつゲートを適用するとどうなるか見て見ます。
Iゲート
単位行列なので何も起きません。何もしないので0から始まった量子ビットは0のままです。
Circuit().i[0].run()
#array([1.+0.j, 0.+0.j])
array([1.+0.j, 0.+0.j])
X,Y,Zゲート
こちらは基本のゲートです。パウリゲートと総称されます。ブロッホ球での軸の回転に相当します。サンプリング実行すると分かりづらいので、状態ベクトルを見て見ます。
Circuit().x[0].run()
#array([0.+0.j, 1.+0.j])
array([0.+0.j, 1.+0.j])
Circuit().y[0].run()
#array([0.-0.j, 1.+0.j])
array([0.-0.j, 0.+1.j])
Circuit().z[0].run()
#array([ 1.+0.j, -0.+0.j])
array([ 1.+0.j, -0.+0.j])
Hゲート
量子重ね合わせを実現する大事なゲートです。アダマールゲートと呼びます。
Circuit().h[0].run()
#array([0.70710678+0.j, 0.70710678+0.j])
array([0.70710678+0.j, 0.70710678+0.j])
Tゲート、Sゲートと、その逆回転
TゲートとSゲートはRZゲートのうちで、回転角が45度と90度に固定されたものです。回転ゲートはアダマールゲートを適用してから見た方がわかりやすいので、見て見ます。
Tゲートは、
Circuit().h[0].t[0].run()
#array([0.70710678+0.j , 0.5 +0.5j])
array([0.70710678+0.j , 0.5 +0.5j])
#これと同じです
import math
Circuit().h[0].rz(math.pi/4)[0].run()
#array([0.70710678+0.j , 0.5 +0.5j])
array([0.65328148-0.27059805j, 0.65328148+0.27059805j])
Tゲートの逆回転がTダガーゲートです。
Circuit().h[0].tdg[0].run()
#array([0.70710678+0.j , 0.5 -0.5j])
array([0.70710678+0.j , 0.5 -0.5j])
#これと同じです
Circuit().h[0].rz(-math.pi/4)[0].run()
#array([0.70710678+0.j , 0.5 -0.5j])
array([0.65328148+0.27059805j, 0.65328148-0.27059805j])
Sゲートは回転角が異なります。
Circuit().h[0].s[0].run()
#array([0.70710678+0.j , 0. +0.70710678j])
array([0.70710678+0.j , 0. +0.70710678j])
#これと同じです
Circuit().h[0].rz(math.pi/2)[0].run()
#array([7.07106781e-01+0.j , 4.32978028e-17+0.70710678j])
array([0.5-0.5j, 0.5+0.5j])
Sゲートの逆回転がSダガーゲートです。
Circuit().h[0].sdg[0].run()
#array([7.07106781e-01+0.j , 4.32978028e-17-0.70710678j])
array([7.07106781e-01+0.j , 4.32978028e-17-0.70710678j])
#これと同じです
Circuit().h[0].rz(-math.pi/2)[0].run()
#array([7.07106781e-01+0.j , 4.32978028e-17-0.70710678j])
array([0.5+0.5j, 0.5-0.5j])
CX、CNOTゲート
こちらは、条件付きのゲートと呼ばれ、コントロールビットとターゲットビットがあります。コントロールビット側が1の時にXゲートを適用します。ビットの指定は2箇所あり、最初がコントロールビット、次がターゲットビットです。
Circuit().cx[0,1].run()
#array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
#コントロールビット側を1にして見ます。
Circuit().x[0].cx[0,1].run()
#array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
#cnotでも全く同じです。
Circuit().x[0].cnot[0,1].run()
#array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
CZゲート
CZゲートはコントロールビットが1の時にターゲットビットにZゲートを適用します。
Circuit().cz[0,1].run()
#array([ 1.+0.j, 0.+0.j, 0.+0.j, -0.+0.j])
array([ 1.+0.j, 0.+0.j, 0.+0.j, -0.+0.j])
#コントロールビット側を1にして見ます。
Circuit().x[0].cz[0,1].run()
#array([ 0.+0.j, 1.+0.j, 0.+0.j, -0.+0.j])
array([ 0.+0.j, 1.+0.j, 0.+0.j, -0.+0.j])
RX,RY,RZ,Phaseゲート
こちらはそれぞれの軸周りでの任意回転です。量子ビットの指定の前に角度の指定が必要になります。RZとPhaseは同じです。
Circuit().rx(math.pi/4)[0].run()
#array([0.92387953+0.j , 0. -0.38268343j])
array([0.92387953+0.j , 0. -0.38268343j])
Circuit().ry(math.pi/4)[0].run()
#array([0.92387953+0.j, 0.38268343+0.j])
array([0.92387953+0.j, 0.38268343+0.j])
#分かりづらいのでHかけてからやります
Circuit().h[0].rz(math.pi/4)[0].run()
#array([0.70710678+0.j , 0.5 +0.5j])
array([0.65328148-0.27059805j, 0.65328148+0.27059805j])
#RZと同じ
Circuit().h[0].phase(math.pi/4)[0].run()
#array([0.70710678+0.j , 0.5 +0.5j])
array([0.70710678+0.j , 0.5 +0.5j])
CRX,CRY,CRZゲート
こちらは軸周りの任意回転ゲートを制御ゲート化したものです。コントロールビットとターゲットビットの指定が必要になります。
#コントロールビットが0だと何も起きません。
Circuit().crx(math.pi/4)[0,1].run()
#array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
Circuit().x[0].crx(math.pi/4)[0,1].run()
array([0. +0.j , 0.92387953+0.j ,
0. +0.j , 0. -0.38268343j])
Circuit().x[0].cry(math.pi/4)[0,1].run()
#array([0. +0.j, 0.92387953+0.j, 0. +0.j, 0.38268343+0.j])
array([0. +0.j, 0.92387953+0.j, 0. +0.j, 0.38268343+0.j])
Circuit().x[0].h[1].crz(math.pi/4)[0,1].run()
#array([0. +0.j , 0.70710678+0.j , 0. +0.j , 0.5 +0.5j])
array([0. +0.j , 0.65328148-0.27059805j,
0. +0.j , 0.65328148+0.27059805j])
#CRZと同じ
Circuit().x[0].h[1].cphase(math.pi/4)[0,1].run()
#array([0. +0.j , 0.70710678+0.j , 0. +0.j , 0.5 +0.5j])
array([0. +0.j , 0.70710678+0.j , 0. +0.j , 0.5 +0.5j])
U1,U2,U3ゲート
角度を指定して実行できるゲートです。指定できる角度の数によってゲートセットが異なります。
U1(lambda)
U2(phi,lambda)
U3(theta,phi,lambda)
Circuit().h[0].u1(0.1)[0].run()
#array([0.70710678+0.j , 0.70357419+0.07059289j])
array([0.70622308-0.03534061j, 0.70622308+0.03534061j])
Circuit().h[0].u2(0.1,0.2)[0].run()
#array([ 0.09983342+0.j , -0.09933467+0.99003329j])
array([-0.00498959-0.09970865j, 0.99376067+0.04972948j])
Circuit().h[0].u3(0.1,0.2,0.3)[0].run()
#array([0.672542 +0.j , 0.64895946+0.35572302j])
array([0.64897187-0.17648868j, 0.71956476+0.17295609j])
CU1,CU2,CU3ゲート
これらは上記のU1,U2,U3ゲートを制御ゲート化したものです。使い方は他のゲートと同じです。
Circuit().h[1].cu1(0.1)[0,1].run()
#array([0.70710678+0.j, 0. +0.j, 0.70710678+0.j, 0. +0.j])
array([0.70688582-0.01767583j, 0. +0.j ,
0.70688582-0.01767583j, 0. +0.j ])
#0番目を1にしてから、
Circuit().x[0].h[1].cu1(0.1)[0,1].run()
array([0. +0.j , 0.70688582-0.01767583j,
0. +0.j , 0.70511898+0.0529833j ])
Circuit().x[0].h[1].cu2(0.1,0.2)[0,1].run()
array([ 0. +0.j , -0.00498959-0.09970865j,
0. +0.j , 0.99376067+0.04972948j])
Circuit().x[0].h[1].cu3(0.1,0.2,0.3)[0,1].run()
array([0. +0.j , 0.64897187-0.17648868j,
0. +0.j , 0.71956476+0.17295609j])
Swapゲート
こちらは2つの量子ビットの値を入れ替えます。簡単です。量子ビットの指定は2つあります。
Circuit().x[0].swap[0,1].m[:].run(shots=1)
#Counter({'01': 1})
Counter({'01': 1})
CCX,Toffoliゲート
CCXとToffoliは同じゲートですが、コントロールビットが2つあります。両方が1の時だけ、ターゲットビットにXゲートを適用します。指定はコントロールビットを2つ指定し、ターゲットビットを指定します。
Circuit().x[:2].ccx[0,1,2].run()
#array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
#全く同じです
Circuit().x[:2].toffoli[0,1,2].run()
#array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
測定ゲート
測定ゲートは測定をします。測定をすると0か1かに計算結果が決まります。
Circuit().h[0].m[:].run(shots=100)
#Counter({'1': 50, '0': 50})
Counter({'0': 53, '1': 47})
Circuit().h[0].measure[:].run(shots=100)
#Counter({'0': 44, '1': 56})
Counter({'0': 53, '1': 47})
最後に
ゲートセットは基本的なものは網羅されてこの一覧を使えばいくらでも作れます。以上です。