モンテカルロ法による3体相互作用を持つ全結合イジング模型の解析
はじめに
この記事はJij Inc. Advent Calendar 2023の22日目の記事です。
株式会社Jijの鈴木です。
この記事では、3体相互作用を持つ全結合イジング模型を古典モンテカルロ法を用いて数値的に解析します。以前に書いた記事で、3体相互作用を含む一般の高次相互作用を持つ全結合イジング模型に対して解析的なアプローチを行い、1次相転移を起こすことが分かりました。つまりこのモデルの性質はある程度分かっているわけですが、この記事ではそのことを数値的に検証します。
モデル
3体相互作用を持つ全結合イジング模型は以下で定義されます。
ここで、
以下ではこのモデルの数値シミュレーション結果を示します。
モンテカルロ法による結果
シミュレーション手法として、シングルスピンフリップによるメトロポリス法[1]を用いました。この手法の解説は行いませんが非常に基本的なアルゴリズムの一つです。以下ではこの手法によるシミュレーション結果を示します[2]。
磁化
この系の磁化
ここで、スピンの期待値
となります。
磁化
さて、この磁化を直接計算せずにモンテカルロシミュレーションで推定するには以下の式を用います。
ここで、
は
3体全結合イジング模型に対するメトロポリス法による磁化の計算結果(左図)とその理論値(右図)。メトロポリス法のサンプル数は
左の図がシミュレーション結果であり、標準誤差をエラーバーとして付けていますが非常に小さいです。右の図は以前に計算した熱力学極限(
3体全結合イジング模型に対するメトロポリス法による転移温度近傍の磁化の計算結果。メトロポリス法のサンプル数は
系の大きさ
ビンダー比
転移温度を検出するために以下のビンダー比の計算を行いました。
ここで、磁化の2乗と4乗の期待値はそれぞれ
と定義されます。この量は相転移点直上でシステムサイズ依存性がなくなる量であり、この量をプロットして交わったところが転移点ということになります。最も今回のケースでは上で見たように磁化
3体全結合イジング模型に対するメトロポリス法による転移温度近傍のビンダー比
確かに
OpenJijを用いた計算
この節では上で行った計算結果を再現するためのソースコードを示します。株式会社Jijが開発しているオープンソースソフトウェアであるOpenJijを用いたコードを紹介します。ただし、OpenJijを用いた場合、ここで考えている3体全結合イジング模型ではメトロポリス法によって一つのスピンをアップデートする際に
実は、3体全結合イジング模型に特化した計算を行えば一つのスピンのアップデートを
シミュレーション
OpenJijの使い方についてはこちらを参照してください。ここでは詳細な説明は行いません。必要なライブラリ、openjij
、numpy
、tqdm
はあらかじめインストールしておいてください。
まず、最も重要なシミュレーション部分のコードを示します。
なお、以下でnum_threads=4
という部分がありますが、このオプションを指定することで各サンプリングを並列に行ってくれます。ただしMac環境はこれに対応していません[7]。
import openjij as oj
import numpy as np
from tqdm import tqdm # プログレスバー表示するためのライブラリ
num_samples = 1000 # サンプル数。計算が遅いので小さくしてあります。
num_sweeps = 1000 # サンプリング数。計算が遅いので小さくしてあります。
T_list = np.linspace(0.1, 1.1, 11) # 温度のリスト
N_list = [20, 30, 40, 50] # システムサイズNのリスト
sample_list_N = []
for N in N_list:
# 相互作用の格納
J = {}
for i in range(N):
for j in range(i+1, N):
for k in range(j+1, N):
J[(i, j, k)] = -2/((N - 1)*(N - 2))
# サンプリング
sample_list = []
for T in tqdm(T_list):
sample = oj.SASampler().sample_hubo(
J=J,
num_sweeps=num_sweeps,
num_reads=num_samples,
vartype="SPIN",
beta_max=1/T,
beta_min=1/T,
num_threads=4
)
sample_list.append(sample)
sample_list_N.append(sample_list)
ちなみにRyzen7950xを用いて32並列(上記のコードでnum_threads=32
とした)で計算を行ったところ、私の環境では全体で2分30秒かかりました。
磁化とビンダー比
サンプリングが終了したら各物理量を計算します。系の磁化
def calculate_magnetization(sample: oj.Response):
# 磁化を計算するして、平均と標準誤差を返す
mag_list = [np.mean(r[0]) for r in sample.record]
return np.mean(mag_list), np.std(mag_list)/np.sqrt(len(mag_list))
def calculate_binder_ration(sample: oj.Response, split = 10):
# サンプルを(デフォルトで)10ごとに区切って各区間でビンダー比を計算し、
# その平均と標準偏差を返す
binder_list = []
for i in range(split):
split_start = i*len(sample.record)//split
split_end = (i + 1)*len(sample.record)//split
m_list = [np.mean(s[0]) for s in sample.record[split_start:split_end]]
m2 = np.mean([m**2 for m in m_list])
m4 = np.mean([m**4 for m in m_list])
binder_list.append(1 - m4/(3*m2**2))
return np.mean(binder_list), np.std(binder_list)
グラフの描画
これらの関数を使って、以下のようにグラフを書きました。matplotlib
をインストールする必要があります。
import matplotlib.pyplot as plt
# 磁化の描画
mag_1 = [calculate_magnetization(sample) for sample in sample_list_N[0]]
mag_2 = [calculate_magnetization(sample) for sample in sample_list_N[1]]
mag_3 = [calculate_magnetization(sample) for sample in sample_list_N[2]]
mag_4 = [calculate_magnetization(sample) for sample in sample_list_N[3]]
plt.errorbar(T_list, [m[0] for m in mag_1], yerr=[m[1] for m in mag_1], marker='o', capsize=4, fillstyle='none', color=[0,0,0], label='$N=20$')
plt.errorbar(T_list, [m[0] for m in mag_2], yerr=[m[1] for m in mag_2], marker='^', capsize=4, fillstyle='none', color=[1,75/255,0/255], label='$N=30$')
plt.errorbar(T_list, [m[0] for m in mag_3], yerr=[m[1] for m in mag_3], marker='s', capsize=4, fillstyle='none', color=[0,90/255,255/255], label='$N=40$')
plt.errorbar(T_list, [m[0] for m in mag_4], yerr=[m[1] for m in mag_4], marker='p', capsize=4, fillstyle='none', color=[3/255, 175/255, 122/255], label='$N=50$')
plt.xlabel('$T$')
plt.ylabel(r'$m_{\rm MC}$')
plt.axvline(x=0.5, color=[0,0,0], linestyle='--')
plt.legend()
plt.show()
# ビンダー比の描画
bind_1 = [calculate_binder_ration(r) for r in sample_list_N[0]]
bind_2 = [calculate_binder_ration(r) for r in sample_list_N[1]]
bind_3 = [calculate_binder_ration(r) for r in sample_list_N[2]]
bind_4 = [calculate_binder_ration(r) for r in sample_list_N[3]]
plt.errorbar(T_list, [m[0] for m in bind_1], yerr=[m[1] for m in bind_1], capsize=4, fillstyle='none', marker='o', color=[0,0,0], label='$N=20$')
plt.errorbar(T_list, [m[0] for m in bind_2], yerr=[m[1] for m in bind_2], capsize=4, fillstyle='none', marker='^', color=[1,75/255,0/255], label='$N=30$')
plt.errorbar(T_list, [m[0] for m in bind_3], yerr=[m[1] for m in bind_3], capsize=4, fillstyle='none', marker='s', color=[0,90/255,255/255], label='$N=40$')
plt.errorbar(T_list, [m[0] for m in bind_4], yerr=[m[1] for m in bind_4], capsize=4, fillstyle='none', marker='p', color=[3/255, 175/255, 122/255], label='$N=50$')
plt.axvline(x=0.5, color=[0,0,0], linestyle='--')
plt.xlabel('$T$')
plt.ylabel(r'$U_{\rm MC}$')
plt.legend()
plt.show()
まとめ、今後について
3体相互作用を持った全結合イジング模型に対してモンテカルロ法を用いて磁化とビンダー比の温度依存性を計算しました。結果、磁化とビンダー比の振る舞いからこの系が
今回は参考としてOpenJijを用いたサンプルコードを載せましたが、全結合イジング模型に限定すればより高速にシミュレーションを行うことができます。次回はそれについて書こうと思います。
\Rustエンジニア・数理最適化エンジニア募集中!/
株式会社Jijでは、数学や物理学のバックグラウンドを活かし、量子計算と数理最適化のフロンティアで活躍するRustエンジニア、数理最適化エンジニアを募集しています!
詳細は下記のリンクからご覧ください。皆さんのご応募をお待ちしております!
Rustエンジニア: https://open.talentio.com/r/1/c/j-ij.com/pages/51062
数理最適化エンジニア: https://open.talentio.com/r/1/c/j-ij.com/pages/75132
-
本来はサンプル数やsweep数が足りているか議論する必要がありますが、この記事ではそのようなことは行っていません。何故かと言えばそれはとても面倒、、、というのが本音ですが、あえて言えばここでの目的はラフに相転移の傾向を捉えることであり、実際に相転移の傾向は見えています。 ↩︎
-
シミュレーション結果に対して
無限大の結果を外挿することで、N が大きい極限の振る舞いを見ることができます。 ↩︎N -
例えば、Phys. Rev. B 30, 1477の図4,Phys. Rev. E 101, 052102の図8、Phys. Rev. B 103, 075147の図2(c)、
Phys. Rev. E 99, 023309の図9、10、13、14などで同様の傾向が見られます。 ↩︎ -
これはOpenJijの実装が悪いわけではなく汎用性のためです。OpenJijは一般の高次相互作用かつ任意の相互作用係数を持ったイジング模型を受け入れることができます。 ↩︎
-
p体全結合イジング模型に対して
で計算することができます。 ↩︎O(pN) -
Macの場合でも、OpenMPを自分で入れてOpenJijのcmakeを適切に書き換えれば並列化が有効になります。 ↩︎
Discussion