汎用型マシンで足し算の演算を二種類でやってみるハンズオンを実践してみたいと思います。
$a+b=x$という式を解きますが、最小値問題に落とし込み、
$(x-a-b)^2 = 0$というのを解きます。
例題として$2+1$をやってみます。
$(x-2-1)^2 = x^2-6x+9$となりますが、$x=q_0+2q_1$とおいて、
$(q_0+2q_1)^2-6(q_0+2q_1)+9=q_0^2+4q_0q_1+4q_1^2-6q_0-12q_1+9$
ここで、$q^2=q$より、
$q_0+4q_0q_1+4q_1-6q_0-12q_1+9=-5q_0-8q_1+4q_0q_1+9$
こちらをQAOAというアルゴリズムにかけていくと最小基底状態を求めることができます。今回は組み立てたハミルトニアンはイジングではなく、
{0,1}の値をとるQUBOというものなので、読み込むパウリオペレータ対応もQUBOとしてqubo_bit as qとして読み込みます。
QAOAに関しては下記を参照ください。
「量子ゲートで組合せ最適化問題を解くQAOAの実装」
http://blog.mdrft.com/post/229
実際のコードはハミルトニアンを記述して、精度を決めるステップ数を決めてから、最終的にQAOAにかけて答えを得ます。
from blueqat import vqe
from blueqat.pauli import qubo_bit as q
hamiltonian = -5*q(0)-8*q(1)+4*q(0)*q(1)
step = 2
result = vqe.Vqe(vqe.QaoaAnsatz(hamiltonian, step)).run()
print(result.most_common(12))
(((1, 1), 0.8650616967371565), ((0, 1), 0.1338735388595646), ((1, 0), 0.0010293765480403065), ((0, 0), 3.538785523831449e-05))
答えは上記のように1,1が出てきました。答えは1+2*1=3となり正解です。
普通の桁上がりの加算器をつくって解きます。こちらの方が一般的かと思います。
今回はとても単純な数字でやってみます。次回以降は桁上がりが任意の加算器を作ってみたいと思います。
まずはa+b=xと言う回路を作ります。
aは1量子ビット、bはb0とb1で2量子ビット使用して、b0+2*b1というかたちにします。xは同様に2量子ビット使用して、x0+2*x1と言う形にしたいと思います。
では、5量子ビット用意して縦に並べます。
次に桁上がりを実装します。
ここではCCNOTと呼ばれるゲートを利用します。トフォリゲートとも呼ばれます。
CCNOTゲートはコントロールゲートが2つあり、それぞれが両方とも1の時、ターゲットゲートを反転させます。
これを使うことで、aとb0の足し算の桁上がりを実現できます。
1、aとb0をx1につなげて、aとb0の両方が1の時にx1を反転させます。
2、b1とx1をCNOTでつなげて、b1の値をx1に継承
3、aとb0をそれぞれx0にCNOTでつなげて値を継承
こちらで実現できます。
こちらにaやbの値を設定すると計算を実現できます。
この計算機では、0+0,0+1,1+0,1+1,0+2くらいまでは計算できます。
ちなみにCCNOTもしくはトフォリゲートは通常の実機ではそのままでは実装できませんので、二量子ビット同士の回路に分解する必要があります。
ということで先にトフォリゲートを確認します。
トフォリゲートはH,CNOT,Tとかで実現され、下記のようになります。
コードで書くと、
from blueqat import Circuit
import math
c = Circuit()
c.h[2].cx[1,2].rz(-math.pi/4)[2].cx[0,2].rz(math.pi/4)[2].cx[1,2].rz(-math.pi/4)[2].cx[0,2].rz(-math.pi/4)[1].rz(math.pi/4)[2].cx[0,1].h[2].rz(math.pi/4)[0].rz(-math.pi/4)[1].cx[0,1].m[0].m[1].m[2].run()
array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
こうなります。
加算器は最終的にこのトフォリゲートを使って、tofにトフォリゲート、afterにトフォリゲート以降のCNOTの回路、measureに全量子ビットの測定を入れて、
from blueqat import Circuit
import math
c = Circuit()
tof = Circuit().h[4].cx[1,4].rz(-math.pi/4)[4].cx[0,4].rz(math.pi/4)[4].cx[1,4].rz(-math.pi/4)[4].cx[0,4].rz(-math.pi/4)[1].rz(math.pi/4)[4].cx[0,1].h[4].rz(math.pi/4)[0].rz(-math.pi/4)[1].cx[0,1]
after = Circuit().cx[2,4].cx[0,3].cx[1,3]
measure = Circuit().m[:]
e = tof + after + measure
e.run()
array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,
0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
こちらの回路に任意のXを加えてみると、
c01 = Circuit().x[1]
c10 = Circuit().x[0]
c11 = Circuit().x[0].x[1]
c02 = Circuit().x[2]
c12 = Circuit().x[0].x[2]
こういうのを作って、回路の前に加えてみてきちんと計算できるかどうかをみてみます。
e = c01+tof+after+measure
e.run()
e.last_result()
(0, 1, 0, 1, 0)
これはOK
e = c10+tof+after+measure
e.run()
e.last_result()
(1, 0, 0, 1, 0)
これもOK
e = c11+tof+after+measure
e.run()
e.last_result()
(1, 1, 0, 0, 1)
これもOK
e = c02+tof+after+measure
e.run()
e.last_result()
(0, 0, 1, 0, 1)
これもOK
e = c12+tof+after+measure
e.run()
e.last_result()
(1, 0, 1, 1, 1)
全部OKでした。
これにより加算器がトフォリゲートとCNOTで実現できました。
実装はトフォリゲートをTゲートとCNOT,Hに分解する必要がありました。
info@mdrft.com