量子ゲートブログ6:QAOAを「自作ループ」で実装する:VQEクラスを卒業して最適化を可視化しよう

こんにちは、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行列を、このようにゲート型のハミルトニアンに変換する作業は、量子計算の本質を理解するのにとても役立ちます。研究の進捗に合わせて、またコードをアップデートしていきます。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です