13 Step 7: 温度スキャンと相転移
このステップは小さな科学的成果である。これまで構築したコードを使って、温度変化に伴うシステムの変化を観察する。
13.1 目的
簡単な温度スキャンを実行し、Plots.jl で <M^2> をプロットし、クロスオーバー領域が厳密な臨界温度とおおむね整合するか確認する。
13.2 到達目標
このステップの終わりまでに、以下ができていること:
TとLを受け取る関数がある- 平均化された
E、M、M^2を格納するDictがある - 温度の関数としての
<M^2>のプロットがある <M^2>が<M>よりもここで有用な理由の基本的な説明ができる- 厳密な 2次元イジング臨界温度との大まかな比較ができる
Step 6 で src/ と test/ を package 本体として整理したなら、このステップの温度スキャンとプロットは analysis/temperature_scan.jl のような解析用スクリプトに置くとよい。こうすると再利用可能な計算コードと、目的別の実験コードを分けられる。
Step 4 で IsingModel 構造体を導入した場合、ここでも活用できる。ただし、immutable struct では beta を後から変更できないため、温度ごとに新しい IsingModel を作成するか、以下の最小限バージョンのように spins と beta を別々に渡す方法でもよい。どちらのスタイルでも問題ない。
13.3 実行スペル
Step 6 で package 化した 2次元イジングモデルのコードを使って、
`analysis/temperature_scan.jl` のような解析用スクリプトを追加せよ。
温度 T と格子サイズ L を受け取り、短いモンテカルロシミュレーションを実行し、
E、M、M^2 の平均を含む Dict を返す簡単な関数を書け。
次にいくつかの温度で評価し、Plots.jl を使って <M^2> vs T をプロットせよ。
package 本体にはプロット処理を入れず、解析スクリプト側に置け。
コードはシンプルで読みやすくせよ。
13.4 説明スペル
今実装したものを説明せよ。E、M、M^2 の平均がどう計算されるか、
なぜ <M^2> が相転移を素早く見るために <M> より有用なのか、
プロットが何を示すと期待されるか、厳密な臨界温度が何か、
この計算がなぜ Tc を正確に決定できないか、
なぜこのコードを `src/` ではなく `analysis/` に置くのかを説明せよ。
13.5 習得する概念
- モンテカルロサンプルにわたる物理量の平均
- 温度スキャン
<M>がほぼゼロに平均化されうる理由- 有限サイズシミュレーションで
<M^2>の方が読みやすい理由 Plots.jlによるプロット- 厳密な
T_cvs 大まかな数値的整合性
13.6 最小限のバージョン
このスキャンは、コードの最適化状況や L の値によって数十秒から数分かかることがある。プログラムが止まったように見えても、辛抱強く待て。L を小さくするか n_meas を減らせば高速化できるが、結果のノイズは増える。
function measure_averages(T, L; n_therm=1_000, n_meas=5_000)
beta = 1 / T
spins = initialize_spins(L)
for _ in 1:n_therm
metropolis_step!(spins, beta)
end
e_sum = 0.0
m_sum = 0.0
m2_sum = 0.0
for _ in 1:n_meas
metropolis_step!(spins, beta)
e = energy(spins) / length(spins)
m = magnetization(spins)
e_sum += e
m_sum += m
m2_sum += m^2
end
return Dict(
:E => e_sum / n_meas,
:M => m_sum / n_meas,
:M2 => m2_sum / n_meas,
)
end次にこのような簡単なスキャンを試せ:
using Plots
temperatures = range(1.5, 3.5; length=16)
L = 16
results = [measure_averages(T, L) for T in temperatures]
m2_values = [r[:M2] for r in results]
Tc_exact = 2 / log(1 + sqrt(2))
plot(temperatures, m2_values;
xlabel="T",
ylabel="<M^2>",
label="L = 16",
lw=2,
)
vline!([Tc_exact]; label="exact Tc", ls=:dash)この小さな計算から鋭い特異性を期待すべきではない。厳密な臨界温度の近くで振る舞いが大まかに変化することだけを期待すべきである。
13.7 結果の読み方
AI に以下の点を説明させるとよい:
<M^2>が低温で大きくなる傾向がある理由- 高温で小さくなる理由
<M>自体がここではあまり安定した視覚的指標にならない理由- 有限格子と短い実行が転移をぼやけさせる理由
- 「厳密な
T_cと矛盾しない」が「T_cを正確に測定した」よりも良い主張である理由
13.8 コードリーディング確認
以下のコード断片を読め:
m = magnetization(spins)
m2_sum += m^2そして以下の質問に答えよ:
mだけでなくm^2を蓄積するのはなぜか?- 有限シミュレーションでは、臨界領域以下でも
<M>が小さくなりうるのはなぜか? - 厳密な
T_cとのどの程度の一致がここでは妥当と言えるか?
13.9 追加テスト生成プロンプト
Julia での 2次元イジングモデルの温度スキャンと <M^2> のプロットについて、
短いコードリーディング問題を3つ作成せよ。
以下に焦点を当てよ:
- 平均化関数が何を返すか
- なぜ M だけでなく M^2 をプロットするのか
- なぜ数値結果を厳密な臨界温度と緩くしか比較すべきでないのか
まだ答えは出すな。
私が答えた後、回答を採点し、フォローアップ質問を1つ出せ。
13.10 スキップ条件
以下がすでにできるなら、このステップはスキップできる:
- 与えられた
TとLに対して平均化された物理量を返す簡単な関数を書ける Plots.jlで基本的なプロットを作れる- 転移付近での
<M^2>が妥当な素早い物理量である理由を説明できる - この種の計算がなぜ
T_cを正確に決定できないかを説明できる
ここまでは 1 つの ising-project の中で package 本体と analysis/ を分ける形で十分である。しかし、よりはっきり責務を分けたいなら、計算 package と解析用環境を別ディレクトリに分ける構成も有力である。
ising-projects/
├── Ising2D/
│ ├── Project.toml
│ ├── src/
│ └── test/
└── analysis/
├── Project.toml
└── temperature_scan.jl
この構成では、Ising2D/ は計算の核だけを持つ Julia package であり、analysis/ はそれを使って温度スキャンやプロットを行う別の Julia 環境である。こうすると Plots.jl のような解析専用の依存性を Ising2D から排除できる。
ヒント:
analysis/の環境ではPlotsなど解析に必要な package だけを追加するIsing2Dをローカル依存として使うには、analysis/でPkg.develop(path="../Ising2D")を試すとよい- その後は
analysis/temperature_scan.jlからusing Ising2Dと書けるか確認せよ
この構成は実務的にはかなり自然であるが、演習としては環境が 2 つになるぶん少し複雑になる。本編では 1 つのプロジェクトで完結させ、余裕があればこの形を試すのがよい。
最後に AI に Pkg.develop と Pkg.add の違いを説明させ、Project.toml と Manifest.toml がどう変化するかまで言語化しておくとよい。
Julia の Pkg における `Pkg.develop` と `Pkg.add` の違いを説明せよ。
特に以下を含めよ:
- ローカルパスを指定した場合に何が起きるか
- `Project.toml` と `Manifest.toml` のどこがどう変わるか
- 再現性と共同開発の観点で、いつどちらを選ぶべきか
- `analysis/` のような解析用環境から `Ising2D/` のような計算用 package を使うときの推奨パターン
最後に、`Pkg.status()` と `Project.toml` / `Manifest.toml` を見て自分で確認する手順を箇条書きで示せ。