量子ゲートブログ2: Qiskitでハミルトニアンを手動構築する(ブログ1の続きでレベル2を解きます)

前回は、QUBO式をそのままライブラリに投げる「レベル1」のコードを解説しました。今回は一歩踏み込んで、量子ビットの物理的な振る舞いを直接指定する「レベル2」の世界を解説します。

1. レベル2とは何か?

レベル1では、ライブラリが裏側で「数式 → 行列(イジングモデル)」への変換を自動で行っていました。しかし、実務や研究で新しいアルゴリズムを開発する場合、自分でハミルトニアン(エネルギーの式)を量子ゲートの組み合わせとして定義する必要があります。

レベル2の核心は、「QUBOを (1-Z)/2または(I-Z)/2 (どっちでも変わらない)で展開し、整理した後の最終形態ZiZjを直接Qiskitに教え込むこと」にあります。


2. 実装コード(重み付きMax-Cut問題)

以下のコードは、4つの拠点(ノード)間のルートに「重み(重要度)」がある場合の最適化問題を解くものです。

import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit_algorithms import QAOA
from qiskit_algorithms.optimizers import COBYLA
from qiskit.primitives import StatevectorSampler as Sampler

# 1. ネットワーク(グラフ)の定義
num_nodes = 4
# (ノードi, ノードj, 重みW)
edges = [
    (0, 1, 3),
    (1, 2, 2),
    (2, 3, 4),
    (3, 0, 1),
    (0, 2, 5)
]

# 2. ハミルトニアンの手動構築(レベル2の核心)
pauli_list = []

for i, j, weight in edges:
    # 全量子ビットを「何もしない(I)」で初期化
    pauli_str_list = ["I"] * num_nodes
    
    # 相互作用する(エッジがある)ノードだけを「Z測定」に指定
    pauli_str_list[i] = 'Z'
    pauli_str_list[j] = 'Z'
    
    # Qiskitの読み順(右から左)に合わせて反転(重要!)
    pauli_str_list.reverse()
    pauli_str = "".join(pauli_str_list)
    
    # (行列の文字列, 係数) のペアをリストに追加
    pauli_list.append((pauli_str, weight))

# 3. 巨大な行列 H (ハミルトニアン) を生成
hamiltonian = SparsePauliOp.from_list(pauli_list)
print("--- 構築されたハミルトニアン ---")
print(hamiltonian)

# 4. QAOAの実行
sampler = Sampler()
optimizer = COBYLA(maxiter=100)
qaoa = QAOA(sampler=sampler, optimizer=optimizer, reps=2)
result = qaoa.compute_minimum_eigenvalue(hamiltonian)

print("\n最適解のビット列:", result.best_measurement['bitstring'])

3. 数式とコードの「翻訳」解説

なぜこのコードで量子コンピュータが問題を理解できるのでしょうか?3つのポイントで解説します。

① なぜ Z に書き換えるのか?

Max-Cut問題のエネルギー(コスト)は、繋がっている2つのノード i, jの関係性 Zi, Zjだけで決まります。

  • 関係あるノード: Z ゲートを指定(スピンを観測する)。
  • 関係ないノード: Iゲート(Identity:何もしない)を指定。

例えば、ノード0と1のエッジを計算する場合、物理的な数式(テンソル積)は以下のようになります。

$$H_{01} = Z_0 \otimes Z_1 \otimes I_2 \otimes I_3$$ (テンソル積はlateXでしか書けないです。。。。)

コード内の pauli_str_list[i] = 'Z' は、まさにこの「物理の指名手配」をプログラミングで表現しているのです。

② なぜ reverse() が必要なのか?

これはQiskit特有のルール(リトルエンディアン)が原因です。

人間は量子ビットを q0, q1, q2, q3と左から数えますが、Qiskitは右端を q0として読みます。

  • 私たちのリスト:['Z', 'Z', 'I', 'I'] (0, 1番目がZ)
  • そのまま文字列にすると:"ZZII" → Qiskitは「3, 2番目がZ」と誤解する
  • 反転させると: "IIZZ" → Qiskitは右から読み、「0, 1番目がZ」と正しく理解する

③ 単一項(1次の項)はどこへ消えた?

レベル1のQUBO式には xi という単独の項がありましたが、レベル2のイジング形式では Zi 単独の項が登場しません。

これは、QUBO式 xi – 2xixj + xj を イジング変換すると、定数項と相互作用項(ZiZj)だけが残り、1次の項(ZiとZj)が数学的に相殺されて消滅するからです。レベル2では、この「計算後の本質的な形」だけを記述しています。


4. エンジニアとしてどちらが重要か?

「ハミルトニアンを構築する能力(レベル2)」と「量子回路を構築する能力(レベル3)」、どちらが重要でしょうか?

  • レベル2(モデリング能力): 大星物流とのPoCやNEDOのコンペなど、現実のビジネス課題をいかに効率的な数式に落とし込むかという局面で最強の武器になります。
  • レベル3(ハードウェア最適化能力): ノイズの多い現在の量子デバイス(NISQ)で、いかにエラーを抑えて計算を通すか、回路構成からハックする研究者・アーキテクト視点の能力です。

実務家として「量子で問題を解く」なら、まずはレベル2を完璧に使いこなせることが、エンジニアとしての信頼に直結します。


まとめ

レベル2を理解すると、論文に書かれている難解なハミルトニアンの数式を、自分の手で自由にコーディングできるようになります。これこそが、量子ネイティブなエンジニアへの第一歩です。

コメントする

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