こんにちは、naoです。今回も数式が多いため、lateXで失礼します。
最近、Qiskitのアップデート(V2 Primitivesの導入)により、以前の VQE クラスなどを使った書き方が大きく変わりました。
今回は、量子近似最適化アルゴリズム(QAOA)を例に、「ブラックボックス化されていた最適化ループを自分で制御する」方法を解説します。これにより、エネルギーが減少していく様子をグラフ化するなど、より柔軟な解析が可能になります。
今回のステップ:Step 1からStep 4への進化
これまでの実装と何が違うのか、4つのステップで整理してみましょう。
Step 1: ハミルトニアンの構築
以前は手動でパウリ行列を組み合わせていましたが、SparsePauliOp を使うことで、グラフの辺(edges)からコストハミルトニアン $H_C$ をスマートに生成します。
$$H_C = \sum_{(i,j) \in E} Z_i Z_j$$
Step 2: QAOA回路の自動生成
QAOAAnsatz を使用します。これにより、以前のように $e^{-i \gamma H_C}$ や $e^{-i \beta H_B}$ の層を自分で for ループで書く必要がなくなりました。reps=2 と指定するだけで、2層分の回路が自動構成されます。
Step 3: Estimator (V2) への移行
ここが最大の変更点です。Backend を直接叩くのではなく、Primitives (Estimator) を使います。
今回はローカルで高速に動作させるため StatevectorEstimator を採用していますが、ここを書き換えるだけで簡単に実機(IBM Quantum)へ移行できるのがメリットです。
Step 4: 古典最適化ループの「自作」
VQE クラスに丸投げするのではなく、SciPyの minimize 関数を使い、自分でコスト関数を定義してループを回します。これにより、各イテレーションのエネルギー値をリストに保存できるようになり、学習曲線の可視化が可能になります。
実装コード(Python)
以下のコードは、5ノードのグラフ問題をQAOAで解くシミュレータ版です。そのままコピー&ペーストして動作します。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
# Qiskit関連のインポート
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import QAOAAnsatz
from qiskit.primitives import StatevectorEstimator
# ==========================================
# 1. 問題の定義とハミルトニアンの構築
# ==========================================
n = 5
edges = [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]
pauli_list = []
for i, j in edges:
pauli_str_list = ["I"] * n
pauli_str_list[i] = "Z"
pauli_str_list[j] = "Z"
pauli_str_list.reverse() # Qiskitのビット順序(little-endian)に対応
pauli_str = "".join(pauli_str_list)
pauli_list.append((pauli_str, 1.0))
cost_hamiltonian = SparsePauliOp.from_list(pauli_list)
print("--- 構築されたハミルトニアン ---")
print(cost_hamiltonian)
# ==========================================
# 2. QAOA回路の自動生成(QAOAAnsatz)
# ==========================================
# reps=2 は、以前の p=2(2層)と同じ意味です。
ansatz = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2)
# ==========================================
# 3. 実行環境と評価関数の定義
# ==========================================
estimator = StatevectorEstimator()
objective_func_vals = [] # コスト履歴保存用
def cost_func_estimator(params, ansatz, hamiltonian, estimator):
# V2 Primitivesの形式: (回路, 演算子, パラメータ)
pub = (ansatz, hamiltonian, params)
job = estimator.run([pub])
results = job.result()[0]
cost = results.data.evs # 期待値(エネルギー)の抽出
objective_func_vals.append(cost)
return cost
# ==========================================
# 4. 古典最適化ループの実行(SciPyを使用)
# ==========================================
# reps=2 なので、gammaとbetaがそれぞれ2つずつ、計4パラメータ
init_params = [np.pi/2, np.pi/2, np.pi, np.pi]
print("\n--- 最適化を開始します ---")
result = minimize(
cost_func_estimator,
init_params,
args=(ansatz, cost_hamiltonian, estimator),
method="COBYLA",
tol=1e-2,
)
print("\n=== 最適化結果 ===")
print(f"最小エネルギー: {result.fun}")
print(f"最適パラメータ: {result.x}")
# ==========================================
# 5. コスト低下の過程をグラフ化
# ==========================================
plt.figure(figsize=(10, 5))
plt.plot(objective_func_vals, marker='o', linestyle='-', color='b')
plt.xlabel("Iteration")
plt.ylabel("Cost (Energy)")
plt.title("QAOA Optimization Process (V2 Estimator)")
plt.grid(True)
plt.show()
まとめ
今回のコードのポイントは、cost_func_estimator という関数を自前で用意したことです。
これまでは VQE クラスの中で隠蔽されていた「量子回路の実行 → 期待値の計算 → 古典最適化」というプロセスが、自分の手元で制御できるようになりました。
これにより、量子アニーリングの勉強で学んだ「エネルギー散逸」のイメージを、ゲート型量子コンピュータの最適化プロセスとしても視覚的に理解できるようになります。
次は、この最適化されたパラメータを使って、実際にどのようなビット列がサンプリングされるかを確認する Sampler の実装に挑戦してみましょう!
Note:
量子アニーリングのQUBO行列を、このようにゲート型のハミルトニアンに変換する作業は、量子計算の本質を理解するのにとても役立ちます。研究の進捗に合わせて、またコードをアップデートしていきます。