Blueqat 0.4.1の状態ベクトル指定機能について
Blueqat 0.4.1では(ようやく)任意の初期状態を指定して回路を動かせるようになりました。例えば次のように使うことができます。
Blueqatのアップデート
Blueqatの旧バージョンがインストールされている場合は、次のようにアップデートします。
!pip install -U blueqat
Collecting blueqat
Downloading blueqat-0.4.1-py3-none-any.whl (60 kB)
[K |████████████████████████████████| 60 kB 577 kB/s eta 0:00:011
[?25hRequirement already satisfied, skipping upgrade: scipy>=1.1.0 in /opt/conda/lib/python3.8/site-packages (from blueqat) (1.4.1)
Requirement already satisfied, skipping upgrade: numpy~=1.12 in /opt/conda/lib/python3.8/site-packages (from blueqat) (1.19.1)
[31mERROR: openfermionblueqat 0.2.2 has requirement blueqat~=0.3.3, but you'll have blueqat 0.4.1 which is incompatible.[0m
Installing collected packages: blueqat
Attempting uninstall: blueqat
Found existing installation: blueqat 0.3.18
Uninstalling blueqat-0.3.18:
Successfully uninstalled blueqat-0.3.18
Successfully installed blueqat-0.4.1
機能の利用
runの引数にinitial=vec
のように初期ベクトルを指定することで、指定された初期ベクトルから計算を始めることができます。
import numpy as np
from blueqat import Circuit
# まずは、普通に動かす
c = Circuit().h[0].m[0]
c.run(shots=100) # => 0と1が50%ずつ
Counter({'0': 49, '1': 51})
# 状態ベクトルvec = (|0> - |1>)/√2 を用意
vec = np.array([1, -1]) / np.sqrt(2)
# 回路cに状態ベクトルvecを指定→vecにアダマールゲートをかけると、1のみが測定される
c.run(shots=100, initial=vec)
Counter({'1': 100})
次元の一致
状態ベクトルの大きさは、回路の2 ^ 量子ビット数
になっている必要があります。また、状態ベクトルが正規化されていることをblueqat側では確認していませんが、正規化されていない場合、測定などの動作が予期しない結果となることがあるので、正規化されたベクトルを利用することを推奨します。
# 次元が合わないとエラーになります
c.x[3].run(shots=100, initial=np.array([1, 0, 0, 0]))
# 正規化はblueqat側では確認していません。正規化されていることを確認するには、以下のようにします。
# True: 正規化されている
np.allclose(np.vdot(vec, vec), 1)
True
# False: 正規化されていない
vec2 = np.array([1, 1])
np.allclose(np.vdot(vec2, vec2), 1)
False
量子回路を動かした結果を、別の回路に入力する
初期状態ベクトルはnumpyなどで作ることもできますが、blueqat回路の計算結果を流用することもできます。その際、量子ビット数を合わせて回路を実行する必要があります。
# Circuit(2)のようにすることで、1量子ビットしかないc1も2量子ビットで計算を行います
c1 = Circuit(2).x[0]
c2 = Circuit(2).x[1]
c2.run(initial=c1.run())
array([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j])
量子テレポーテーション
blueqatは、量子ビットのクロネッカー積を取る際、右から順にかけられていること(2番目$\otimes
# AliceとBobは、ベル状態を共有しています
bell = Circuit().h[0].cx[0, 1].run()
bell
array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])
# これをテレポーテーションします
psi = np.array([np.cos(0.4), 1j*np.sin(0.4)])
psi
array([0.92106099+0.j , 0. +0.38941834j])
# 系全体としては、bellとpsiのクロネッカー積を取った状態となります
state = np.kron(bell, psi)
state
array([0.65128847+0.j , 0. +0.27536035j,
0. +0.j , 0. +0.j ,
0. +0.j , 0. +0.j ,
0.65128847+0.j , 0. +0.27536035j])
# Alice側: 量子テレポーテーションの回路
alice = Circuit(3).cx[0, 1].h[0].m[0, 1]
# 状態ベクトルとショットの両方を取得します
vec, shot = alice.run(returns="statevector_and_shots", shots=1, initial=state)
vec, shot
(array([0. +0.j , 0. +0.j ,
0. +0.j , 0. -0.38941834j,
0. +0.j , 0. +0.j ,
0. +0.j , 0.92106099+0.j ]),
Counter({'110': 1}))
# Alice→Bobに測定結果を伝えます
measured = shot.most_common()[0][0]
measured
'110'
# Bob側: 量子テレポーテーションの回路
bob = Circuit(3)
if measured[0] == '1':
bob.x[2]
if measured[1] == '1':
bob.z[2]
# Aliceが測定した続きから、Bobが測定をします
result = bob.run(initial=vec)
result
array([ 0. +0.j , 0. +0.j ,
0. +0.j , 0.92106099+0.j ,
-0. +0.j , -0. +0.j ,
-0. +0.j , 0. +0.38941834j])
Bobに状態ベクトルが転送されました。これを確認するには、この状態ベクトルが、テレポーテーションした状態psi
とaliceが手元に持っている状態のクロネッカー積であることを見ればよいです。
alice_state = Circuit(2)
if measured[0] == '1':
alice_state.x[0]
if measured[1] == '1':
alice_state.x[1]
expect = np.kron(psi, alice_state.run())
expect
array([0. +0.j , 0. +0.j ,
0. +0.j , 0.92106099+0.j ,
0. +0.j , 0. +0.j ,
0. +0.j , 0. +0.38941834j])
# 一致していることが分かります
np.allclose(result, expect)
True