はじめに

こちらは以前の記事の焼き直しです。最近のBlueqatが機能追加に伴い、以前よりもプログラミングしやすくなりましたので、簡単に書き直して見ます。一部文章も簡略化しています。

汎用型のSDKのBlueqatで汎用型量子ゲートマシンの多量子ビットの加算器と減算器をしてみたいと思います。


2進数での桁上がり

量子ビットはそれぞれ0と1をとるバイナリ値と呼ばれるものです。こちらを利用して加算(足し算)を行うには主に論理ゲートを使って、桁上がりなどの実現が主になります。


量子ビットを使った数字の表現

10進数は2進数で表現する必要があります。量子ビットを利用して、たとえば、15は

$q_0*2^0+q_1*2^1+q_2*2^2+q_3*2^3$

のとき、$q_0=q_1=q_2=q_3=1$とすれば平気です。


参考

こちらを参考にします。とても良い記事です。

「量子コンピュータ(シミュレータ)でモジュール化可能な加算器を作る」

https://qiita.com/converghub/items/c61b2b91b311cf730e18


まず2量子ビット同士の加算

まずは余分な量子ビットを使った2量子ビットの加算をみていきます。

$\mid a,b,0 \rangle = \mid a,b,a+b \rangle$を実現します。

$a=a_0*2^0+a_1*2^1$と$b=b_0*2^0+b_1*2^1$と$c=c_0*2^0+c_1*2^1+c_2*2^2$を用意します。

a+bをする際に答えのcは1量子ビット増やしておく必要があります。

a0 ----------------

a1 ----------------
b0 ----------------
b1 ----------------
c0 ----------------
c1 ----------------
c2 ----------------

こんな感じで回路をスタートさせます。まずは桁上がりを全て実装してみます。まずはa0,b0の桁上がりをc1に反映します。次にa1,b1,c1の桁上がりを3つのCCNOTを使って反映します。

a0 -*--------------

a1 -|-*-*----------
b0 -*-|-|----------
b1 -|-*-|-*--------
c0 -|-|-|-|--------
c1 -X-|-*-*--------
c2 ---X-X-X--------

そして最後に各くらいの数字を揃えます。

a0 -*-----------*------

a1 -|-*-*---*---|------
b0 -*-|-|---|---|-*----
b1 -|-*-|-*-|-*-|-|----
c0 -|-|-|-|-|-|-X-X----
c1 -X-|-*-*-X-X--------
c2 ---X-X-X------------

これでできました。あとはa0,a1,b0,b1に数字を入れていけば任意の加算ができます。任意の数字はXゲートで入れていきます。

0+0,0+1,1+0,1+1,0+2,2+0,1+2,2+1,2+2,2+3,3+2,3+3あたりまでできます。

トフォリゲートはccx[c,c,x]で、cx回路はcx[c,x]でかけます。また、サーキットを別々で作って足し合わせてrun()すると使えます。下記はデータ入力の部分と実行部分を分けて足し合わせてます。

from blueqat import Circuit 

#加算回路
a=Circuit().ccx[0,2,5].ccx[1,3,6].ccx[1,5,6].ccx[3,5,6].cx[1,5].cx[3,5].cx[0,4].cx[2,4].m[:]

#0+1
(Circuit().x[2] + a).run(shots=100)
Counter({'0010100': 100})

#1+0
(Circuit().x[0] + a).run(shots=100)
Counter({'1000100': 100})

#1+1
(Circuit().x[0,2]).run(shots=100)
Counter({'1010010': 100})

#0+2
(Circuit().x[3]+a).run(shots=100)
Counter({'0001010': 100})

#2+0
(Circuit().x[1]+a).run(shots=100)
Counter({'0100010': 100})

#1+2
(Circuit().x[0,3]+a).run(shots=100)
Counter({'1001110': 100})

#2+1
(Circuit().x[1,2]+a).run(shots=100)
Counter({'0110110': 100})

#2+2
(Circuit().x[1,3]+a).run(shots=100)
Counter({'0101001': 100})

#2+3
(Circuit().x[1:4]+a).run(shots=100)
Counter({'0111101': 100})

#3+2
(Circuit().x[0,1,3]+a).run(shots=100)
Counter({'1101101': 100})

#3+3
(Circuit().x[0:4]+a).run(shots=100)
Counter({'1111011': 100})


減算器について

減算器は加算器を逆にすればいいようです。ただ、実際にどのように動作するのかわからないといけないので、確認してみます。

先ほどの回路を全く逆にしてみます。

a0 ---*-----------*----

a1 ---|---*---*-*-|----
b0 -*-|---|---|-|-*----
b1 -|-|-*-|-*-|-*-|----
c0 -X-X-|-|-|-|-|-|----
c1 -----X-X-*-*-|-X----
c2 ---------X-X-X------

コードで書くと、CNOTが先に来て、CCNOT群が後に来ます。測定して結果を見てみると、ここで具体例で3-3をしてみます。

#減算回路

b =Circuit().cx[2,4].cx[0,4].cx[3,5].cx[1,5].ccx[3,5,6].ccx[1,5,6].ccx[1,3,6].ccx[0,2,5].m[:]

#3-3
(Circuit().x[0,1,4,5] + b).run(shots=100)
Counter({'1100000': 100})
c-aをしていますが、c-aの値がcに上書きされて出てきています。同様にa+b=cc-bをしてみます。

#3-1
(Circuit().x[2,4,5] + b).run(shots=100)
Counter({'0010010': 100})
これもcが上書きされて答えの2がでました。最後にc3ab1をセットしてみます。

#3-1-1
(Circuit().x[0,2,4,5] + b).run(shots=100)
Counter({'1010100': 100})

cに3-1-1がセットされました。

基本的な加算と減算をみました。基本的には上記もしくは上記の改造をすることで、任意の桁数の二進数の数字の加算ができます。トフォリゲートを駆使して0の位から大きな位に計算を進め、再度大きな位から0の位に戻ってCNOTを活用することでできるようです。

コードの改善

今回は大幅なコードの改善に成功しました。

以前は、トフォリを作るのにこんな感じでしたが、

from blueqat import Circuit 

import math
tof1 = Circuit().h[5].cx[2,5].rz(-math.pi/4)[5].cx[0,5].rz(math.pi/4)[5].cx[2,5].rz(-math.pi/4)[5].cx[0,5].rz(-math.pi/4)[2].rz(math.pi/4)[5].cx[0,2].h[5].rz(math.pi/4)[0].rz(-math.pi/4)[2].cx[0,2]
tof2 = Circuit().h[6].cx[3,6].rz(-math.pi/4)[6].cx[1,6].rz(math.pi/4)[6].cx[3,6].rz(-math.pi/4)[6].cx[1,6].rz(-math.pi/4)[3].rz(math.pi/4)[6].cx[1,3].h[6].rz(math.pi/4)[1].rz(-math.pi/4)[3].cx[1,3]
tof3 = Circuit().h[6].cx[5,6].rz(-math.pi/4)[6].cx[1,6].rz(math.pi/4)[6].cx[5,6].rz(-math.pi/4)[6].cx[1,6].rz(-math.pi/4)[5].rz(math.pi/4)[6].cx[1,5].h[6].rz(math.pi/4)[1].rz(-math.pi/4)[5].cx[1,5]
tof4 = Circuit().h[6].cx[5,6].rz(-math.pi/4)[6].cx[3,6].rz(math.pi/4)[6].cx[5,6].rz(-math.pi/4)[6].cx[3,6].rz(-math.pi/4)[5].rz(math.pi/4)[6].cx[3,5].h[6].rz(math.pi/4)[3].rz(-math.pi/4)[5].cx[3,5]
after = Circuit().cx[1,5].cx[3,5].cx[0,4].cx[2,4]
measure = Circuit().m[:]
c01 = Circuit().x[2]
e = c01 + tof1 + tof2 + tof3 + tof4 + after + measure
e.run()
e.last_result()
(0, 0, 1, 0, 1, 0, 0)

新しいのは、

from blueqat import Circuit 

#加算回路
a=Circuit().ccx[0,2,5].ccx[1,3,6].ccx[1,5,6].ccx[3,5,6].cx[1,5].cx[3,5].cx[0,4].cx[2,4].m[:]
#0+1
(Circuit().x[2] + a).run(shots=100)
Counter({'0010100': 100})

これでいけました。みんなもどんどん開発者に要望出そう。以上です。