#VQEを世界一簡単に書く1
VQEの解説記事が、最近、いろいろ世に出てきました。勉強された人なら、少しの試行錯誤で書けるでしょう。
けれど「もっと簡単に書きたい」と思う方も大勢いるかと思います。
私もそういうタイプで、その気持ちが強まりすぎたので、BlueqatにはVQEを世界一簡単に1書ける仕組みを用意しているのですが。
自分で言わないと誰も気づいてくれないので、記事を書きます。
HamiltonianとAnsatzさえあればいい
「何に対して答えを求めるか」は、ユーザが決めざるを得ないので、ハミルトニアンはユーザが作る必要があります。
また、とりあえずどんな回路になりそうかも、今はユーザに決めてもらっています。これはAnsatzと呼ばれます。
QAOA用のAnsatzなど、Blueqat側で用意しているAnsatzもあるのですが、問題に合わせてAnsatzを作ることになるかと思います。
これはユーザに負担を強いることです。ごめんなさい、けど、簡単なので許して下さい。
というわけで、今回は、1量子ビットのVQEを世界一簡単に1書いてみます。
Hamiltonianは、blueqat.pauli
から作れる
ごっつ簡単です。blueqat.pauli
を使えば、
from blueqat.pauli import X, Y, Z, I
h = 1.23 * I - 4.56 * X(0) + 2.45 * Y(0) + 2.34 * Z(0)
のように、積和の形で書けます。
乱数を使って、ランダムにHamiltonianを作ってみる
今回、何か特別に解きたいハミルトニアンがあるわけではないので、乱数を使ってハミルトニアンを作ってみましょう。
from random import random
from blueqat.pauli import X, Y, Z, I
h = random() * I + random() * X(0) + random() * Y(0) + random() * Z(0)
できました。簡単ですね。
#Ansatzはblueqat.vqe.AnsatzBase
を継承してget_circuit
を実装すればできる
任意の1量子ビットを作るには、RYゲートとRZゲートを1回ずつ適用したら十分です。2
そのようなAnsatzを作ってみましょう。
from blueqat import Circuit
from blueqat.vqe import AnsatzBase
class OneQubitAnsatz(AnsatzBase):
def __init__(self, hamiltonian):
AnsatzBase.__init__(hamiltonian, 2) # 親クラスの__init__に、Hamiltonianとパラメータ数を渡す
def get_circuit(self, params):
# RYゲートとRZゲートを適用した回路を返す
a, b = params
return Circuit().ry(a)[0].rz(b)[0]
簡単ですね。
HamiltonianとAnsatzがあればVQEはできる
from blueqat.vqe import Vqe
h = random() * I + random() * X(0) + random() * Y(0) + random() * Z(0)
runner = Vqe(OneQubitAnsatz(h))
result = runner.run()
print('Result by VQE')
print(runner.ansatz.get_energy(result.circuit, runner.sampler))
runner = Vqe(OneQubitAnsatz(h))
で、さっき定義したAnsatzを作って、さらに、VQEを動かすためのrunner
を作っています。
result = runner.run()
で、VQEを動かして、結果を得ることができます。
結果からエネルギーを取り出す処理は、恐らく、この記事で最難関でしょう。覚えないでコピペして下さい。3
runner.ansatz.get_energy(result.circuit, runner.sampler)
これで、エネルギーが取り出せました。ちょっと難しいけれど、まぁまぁ簡単ですね。
本当に答えあってるの? numpyで求めた固有値と比較してみよう
import numpy as np
# Hamiltonian to matrix
mat = h.to_matrix()
# Calculate by numpy
print('Result by numpy')
print(np.linalg.eigh(mat))
blueqat.pauli
の機能で、Hamiltonianをto_matrix()
によりnumpyのarrayに変換できます。4
変換したarrayから、numpyのnp.linalg.eigh
を使って固有値を求めることができます。
通しで実行してみよう
ソース全体はこんな感じ
from random import random
import numpy as np
from blueqat import Circuit
from blueqat.pauli import X, Y, Z, I
from blueqat.vqe import AnsatzBase, Vqe
class OneQubitAnsatz(AnsatzBase):
def __init__(self, hamiltonian):
super().__init__(hamiltonian, 2)
def get_circuit(self, params):
a, b = params
return Circuit().ry(a)[0].rz(b)[0]
# Calculate by VQE
h = random() * I + random() * X(0) + random() * Y(0) + random() * Z(0)
runner = Vqe(OneQubitAnsatz(h))
result = runner.run()
print('Result by VQE')
print(runner.ansatz.get_energy(result.circuit, runner.sampler))
# Hamiltonian to matrix
mat = h.to_matrix()
# Calculate by numpy
print('Result by numpy')
print(np.linalg.eigh(mat))
実行結果はこんな感じ(乱数でHamiltonianを作っているので実行するたびに変わります)
Result by VQE
-0.7972144535545334
Result by numpy
(array([-0.7972159 , 2.24607842]), array([[-0.41818368+0.j , -0.90836249+0.j ],
[ 0.57016708+0.70712935j, -0.26248835-0.32554179j]]))
VQEで求まった固有値は-0.7972144535545334
numpyで求まった固有値(のうち、最小のもの)は-0.7972159
ほぼほぼ一致していることが確認できました。
まとめ
Blueqatを使えば世界一簡単に1VQEが書ける
-
「パラメータ3ついるんじゃなかったっけ?」と思われた方もいるかもしれません。確かに、任意の1量子ビットを作るには、3つのパラメータが必要です。けれど、|0>から、任意の1量子ビット状態|Ψ>を作るには、2つのパラメータで十分です。ブロッホ球を思い浮かべてください。|0>状態なので、針は真上に向いています。それを、Z軸と垂直な、例えばY軸まわりにいくらか回転させて、続いてZ軸まわりにいくらか回転させれば、球上のどこにでも針を向けることができます。 ↩
-
これを書くのがあまりにも面倒であることを、Blueqat開発者は十分に理解しているので、今後、より簡単な形に改良するはずです。 ↩
-
ただし、この処理は、量子ビット数が多いHamiltonianでは非常に重くなります。特に10量子ビットを越えてくると、待ちきれないほど待たされることになるかと思います。あくまで、小さめのHamiltonianのチェック用だとご理解ください。 ↩