量子コンピュータ
量子アニーリング
qubo
KT相転移
リバース量子アニーリング

こちらはノーベル賞のシミュレーション再現論文として有名です。

Observation of topological phenomena in a programmable lattice of 1,800 qubits

https://www.nature.com/articles/s41586-018-0410-x

Reverse Quantum Annealing for Local Renement of Solutions

https://www.dwavesys.com/sites/default/files/14-1018A-A_Reverse_Quantum_Annealing_for_Local_Refinement_of_Solutions.pdf

KT相転移という渦を導入した2次元の磁気相転移のシミュレーションをD-waveを使って行ったというものですが、渦を表現するために角度に古典スピンの上下を利用してやっています。モデルは特殊ですが、アルゴリズムはReverse Annealingという、通常は横磁場ありから横磁場なしへもっていきますが、横磁場なしから横磁場あり、最後に再度横磁場なしへ持っていく手法です。

1.png

リバースアニーリングは通常ランダムから始まる古典スピンの状態を特定の状態からスタートし、横磁場を強くしていきます。そして再度横磁場を弱めることで、再度最終の古典状態を求めます。もしこれを繰り返す場合、強める横磁場の強さによって渡っていく局所解の範囲を決めることができ、連続で最適化を行いながら、求めたいより良い局所解をもとめることができます。

実際にD-Waveをつかってみると、やはり毎回最適解とはいかないという現実的な問題があります。このようなリバースアニーリングを利用して古典状態を連続してシミュレーションしていくのも面白い試みだと思います。


組合せ最適化問題で試してみる

残念ながらwildqatにはReverse Quantum Annealingが搭載されていないので、手作業でやってみます。Jijには10量子ビットから1量子ビット選ぶというQUBOをJij変換したものを使いました。

量子アニーリングでやる元気がなかったので擬似的にSAでやってみました。最初は温度の低いところからqubitを決めて古典状態からスタート、徐々に温度を上げて、指定のところまで上がったら今度は徐々に温度を下げるということを全部で5回繰り返しました。コードと結果は下記で、温度は0.01から3の間。量子ビット数は10量子ビットから1量子ビット選ぶ問題。温度の上昇は1.1倍ずつ。温度の下降は0.9ずつにしてあります。

import numpy as np

import matplotlib.pyplot as plt

def Ei(q3,j3):
EE = 0
for i in range(len(q3)):
EE += q3[i]*j3[i][i] + sum(q3[i]*q3[i+1:]*j3[i][i+1:])
return EE

Tmin = 0.01
Tmax = 3
T = Tmin
N = 10
q = np.random.choice([-1,1],N)
J = np.array([[4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
[0, 4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
[0, 0, 4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
[0, 0, 0, 4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
[0, 0, 0, 0, 4, 0.5, 0.5, 0.5, 0.5, 0.5],
[0, 0, 0, 0, 0, 4, 0.5, 0.5, 0.5, 0.5],
[0, 0, 0, 0, 0, 0, 4, 0.5, 0.5, 0.5],
[0, 0, 0, 0, 0, 0, 0, 4, 0.5, 0.5],
[0, 0, 0, 0, 0, 0, 0, 0, 4, 0.5],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 4]])

Earr = []
repeat = 5
print('first',q)

for i in range(repeat):
while(T < Tmax):
x_list = np.random.randint(0,N,1000)
for x in x_list:
q2 = np.ones(N)*q[x]
q2[x] = 1
dE = -2*sum(q*q2*J[:,x])

if dE < 0 or np.exp(-dE/T) > np.random.random_sample():
q[x] *= -1
Earr.append(Ei(q,J))
T *= 1.1

print('reverse',q)

while(T > Tmin):
x_list = np.random.randint(0,N,1000)
for x in x_list:
q2 = np.ones(N)*q[x]
q2[x] = 1
dE = -2*sum(q*q2*J[:,x])

if dE < 0 or np.exp(-dE/T) > np.random.random_sample():
q[x] *= -1
Earr.append(Ei(q,J))
T *= 0.9

print('minimum',q)

plt.plot(Earr)
plt.show()

#結果
first [ 1 -1 -1 1 -1 -1 1 1 1 -1]
reverse [-1 -1 -1 1 -1 -1 -1 -1 -1 -1]
minimum [-1 -1 -1 -1 -1 -1 -1 -1 1 -1]
reverse [-1 -1 -1 -1 -1 -1 -1 1 1 1]
minimum [-1 -1 -1 -1 -1 -1 -1 -1 -1 1]
reverse [-1 -1 1 -1 -1 -1 1 -1 -1 -1]
minimum [-1 -1 -1 -1 -1 -1 -1 -1 1 -1]
reverse [-1 -1 -1 -1 1 -1 1 -1 1 1]
minimum [-1 -1 -1 -1 -1 -1 -1 -1 1 -1]
reverse [-1 -1 1 -1 -1 -1 -1 -1 -1 -1]
minimum [-1 -1 -1 -1 -1 -1 -1 -1 1 -1]

結果は比較的欲しかった結果に近いと思います。初期のqubitは1と-1がランダムです。1回目のリバースアニーリングで一定のアニーリングがかかっています。そして、再度順方向にアニーリングをかけると基底状態が変わっています。再度リバースをかけて励起させて、再度下げる。また基底状態が変わっていますが、比較的近いところの基底状態を行ったり来たりしています。

リバースをかけたところは励起して、またすぐ落ちるということを繰り返しています。リバース時に近傍の局所解で行ったり来たりできるのでしょうか。ある程度励起するためのパラメータ調整は必要でした。設定次第では、D-Waveのスケッチのように局所解を渡るためにはスケジュールや励起するための横磁場の強さの調整が必要と感じました。

また、予想外に面白かったのは古典から横磁場を増やすreverse時にもアニーリングが効いて状態がよくなるという感じがしました。実際の実機で行う場合には全部で数十msでアニーリングするようなので、それでも十分早いと思います。使いどころはかなり限られそうですが、類似研究をしてもらいたいと思いました。以上です。