Skip to main content
IBM Quantum Platform

AQC + トロッター動力学を用いた中性子散乱のシミュレーション:サーバーレスワークフロー

推定実行時間:Heron r3 プロセッサで 18 分(注:これはあくまで推定値です。 実行時間は状況によって異なる場合があります。)

どのチュートリアルを使えばいいですか?

このチュートリアルでは、回路の構築、圧縮、実行を1つの呼び出しにまとめた、 Qiskit Serverless の組み込み関数を使用して、中性子散乱実験を実行します。 圧縮処理では、サーバーレスワーカーの演算リソースとメモリリソースが使用されます。ノートブックを閉じた後も処理は継続されますが、初期状態の準備と後処理は引き続きローカルで実行されます。 まず、 関数テンプレートをデプロイする必要があります。 実装手順を段階的に学ぶには、 オリジナルのチュートリアルをご利用ください。


学習成果

  • 非弾性中性子散乱スペクトルが、 1D 量子磁石の動的構造因子 S(q,ω)S(q, \omega) にどのように対応するか。
  • 密度行列再正規化群(DMRG)および行列積状態(MPS)のフィデリティ最大化を用いて、 KCuF3_3 (等方性ハイゼンベルク)基底状態を準備する方法。
  • Trotterの時間発展、近似量子コンパイル(AQC)による回路圧縮、および緩和実行を、単一の関数呼び出しとして実行する方法。
  • サイトごとの ⟨σz⟩(t)\langle \sigma_z \rangle(t) の時系列データを S(q,ω)S(q, \omega) に変換し、2つのスピノン連続体を同定するための後処理方法。

前提条件


背景

非弾性中性子散乱では、スピン間相関関数の時空間フーリエ変換である動的構造因子 S(q,ω)S(q, \omega) が測定されるため、微視的なスピンモデルから S(q,ω)S(q, \omega) を再現することは、量子シミュレーションに対する直接的かつ反証可能な検証となる。 このチュートリアルでは、 KCuF3_3 について考察する。これは、スピン- 12\frac{1}{2} の反強磁性ハイゼンベルグ鎖であり、その励起は単一スピンの反転ではなく、分画化されたスピノン対である。 S(q,ω)S(q, \omega) では、鋭いマグノン分散曲線ではなく、 広い2スピノン連続体が示されており、その下限は π2∣sin⁡q∣\tfrac{\pi}{2}|\sin q|、上限は π∣sin⁡(q/2)∣\pi|\sin(q/2)| によって囲まれている。これらは、以下の図中の破線曲線である。 物理的メカニズムの全容および測定された中性子データとの比較については、 元のチュートリアルおよびLeeらによる論文で解説されています arXiv:2603.15608.

量子ワークフローは、散乱実験を反映しています:

  1. チェーンの基底状態 ∣ψ0⟩|\psi_0\rangle を準備する。
  2. 中心地点で局所的な摂動、すなわち π/2\pi/2 ZZ -rotationを加え、中性子からの運動量およびエネルギーの伝達を模倣してみましょう。
  3. トロッター積公式を用いて、ハイゼンベルク・ハミルトニアン e−iHte^{-iHt} の下で時間発展を行う。
  4. サイトごとの磁化 ⟨σzj⟩(t)\langle \sigma_z^j \rangle(t) を測定する。これは、サイト jj および時間 tt の関数として、まさに遅延グリーン関数 GR(j,jc,t)G^R(j, j_c, t) に等しいため、ステップ5でのフーリエ変換の前に変換を行う必要はない。
  5. GRG^R をフーリエ変換すると、 S(q,ω)S(q, \omega) となる。

ステップ3において、長い進化に対する正確なトロッター回路がハードウェアの処理能力を超えて深くなりすぎる場合、問題が生じる可能性があります。 テンソルネットワークを用いたAQCは、トロッターステップのブロックを、固定された浅いパラメータ化アンサッツに圧縮することでこの問題に対処しており、そのアンサッツの正確な進化に対する状態忠実度は、MPSシミュレータを用いて古典的に最大化される( arXiv:2301.08609 )。 AQC Dynamicsテンプレートは、この量子コア全体(トロッター合成、AQC圧縮、およびリスク軽減実行)を1つの呼び出しにまとめ上げています:

PRE(このノート)
FUNCTION (aqc-dynamics-function)
投稿(このノート)
DMRGとMPSによる忠実度最大化を組み合わせた基底状態。中性子キックは、同じ回路に組み込まれているトロッター合成 → AQC圧縮 → statevector、 fake、 または での実行 runtime、サイトごとに ⟨σzj⟩(t)\langle \sigma_z^j \rangle(t) を返すS(q,ω)S(q, \omega)、動的構造因子

実験固有の作業は、このノートブック内に残されています。具体的には、基底状態の準備(PRE)と、 S(q,ω)S(q, \omega) による後処理(POST)です。 量子計算を多用する2つのステップ、すなわち圧縮と実行は、この関数内で実行されます。

このチュートリアルは、「 量子回路を用いた量子材料の中性子散乱のシミュレーション 」の補足資料であり、同記事と同じ実験を順を追って構築しています。具体的には、 KCuF3_3モデル、基底状態の準備、中性子キック、後処理に加え、トロッター合成、AQC圧縮、およびミティゲート実行について、段階的に解説しています。 AQC圧縮の仕組みについて知りたい場合は、そのチュートリアルをお読みください。 デプロイ済みの関数テンプレートを使って同じ実験を実行するには、こちらの記事をご覧ください。量子コアは単一の関数呼び出しとなり、数時間かかるAQC圧縮処理は、ご自身のマシン上ではなくサーバーレスワーカー内で実行されるため、実行中にHPCシステムやオープンカーネルを用意する必要はありません。 この呼び出しは、 1D の他の動的実験も駆動しています。


要件

このチュートリアルを始める前に、以下のものが揃っていることを確認してください:

  • Qiskit Serverless アカウントにデプロイされた関数。 まずコンパニオン関数テンプレートを実行します: AQC + Trotter ダイナミクス関数テンプレートをデプロイして実行します。 このガイドでは、ソースファイルの入手方法と、その関数を自分のアカウントにアップロードする方法について順を追って説明しています。 このチュートリアルでは、デプロイされた関数を呼び出すだけです。

  • IBM Quantum® ( QiskitServerless 関数テンプレートを参照)に保存された認証情報。 このチュートリアルの両方の例では、デプロイされた関数が呼び出されるため、どちらもそれが必要です。

  • Qiskit SDK v2.0 またはそれ以降 (pip install qiskit)。

  • Qiskitの IBM カタログクライアント(pip install qiskit-ibm-catalog)。

  • NumPy, SciPy, また、 Matplotlib (pip install numpy scipy matplotlib) も必要です。 SciPy 基底状態の準備に使用される COBYQA オプティマイザーには、 1.14 以降が必要です。

  • AQCテンソルネットワークスタックは、ステップ1での基底状態の準備が、このノートブック内でローカルに実行されるためです: pip install 'qiskit-addon-aqc-tensor[quimb-jax]==0.3.1'。

新しくデプロイされた関数への最初の呼び出しでは、Serverlessワーカーが依存関係をインストールする間待機するため、その実行時には通常より遅延が生じることを想定してください。


セットアップ

ライブラリをインポートし、後で使用する実験固有のヘルパー関数を定義します: (build_gs_ansatz 基底状態の準備のためのハミルトニアン変分アンザッツ(HVA))、 (prepare_ground_state DMRGおよびMPS忠実度最大化)、ならびに get_spectrum、 plot_green、および plot_spectrum (S(q,ω)S(q, \omega) による後処理)。 これらは、 元の中性子散乱チュートリアルを基に改変したものです。

from functools import partial

import matplotlib.pyplot as plt
import numpy as np
import scipy.optimize

import quimb.tensor as qtn
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
from qiskit_ibm_catalog import QiskitServerless
#  Dynamical structure factor via discrete Fourier transform


def get_spectrum(n, Gjjc, dt, time_steps, q_steps, w_steps):
    """Compute the dynamical structure factor from the retarded Green's function.

    Uses the center-site approximation and a discrete Fourier transform.
    """
    green = Gjjc / 4  # sigma -> S=1/2
    omega_max = np.pi / dt
    qpoints = np.arange(0, 2 * np.pi, 2 * np.pi / q_steps)
    omegas = np.arange(0, omega_max, omega_max / w_steps)
    green_map = np.zeros((omegas.shape[0], qpoints.shape[0]))
    center = n // 2 - 1
    for iw, w in enumerate(omegas):
        exponent = np.exp(1j * w * dt * np.arange(1, time_steps + 1))
        S_w = np.dot(green.T, exponent) * dt
        for iq, q in enumerate(qpoints):
            q_matrix = np.exp(-1j * q * np.arange(-center, center + 2, 1))
            green_map[iw, iq] = np.imag(np.dot(S_w, q_matrix))
    return green_map


#  Plotting helpers


def plot_spectrum(
    dsf,
    dt,
    q_steps,
    w_steps,
    lower_bound=False,
    upper_bound=False,
    title=None,
):
    """Heat-map of the dynamical structure factor."""
    omega_max = np.pi / dt
    qpoints = np.arange(0, 2 * np.pi, 2 * np.pi / q_steps)
    omegas = np.arange(0, omega_max, omega_max / w_steps)
    x, y = np.meshgrid(qpoints, omegas)
    fig, ax = plt.subplots(figsize=(8, 5))
    c = ax.pcolormesh(x, y, dsf / np.max(dsf), cmap="viridis", shading="auto")
    fig.colorbar(c, ax=ax, label="Normalized intensity")
    if lower_bound:
        ax.plot(
            qpoints,
            np.pi * np.abs(np.sin(qpoints)) / 2,
            "--",
            color="white",
            lw=1.5,
            label="Lower bound",
        )
    if upper_bound:
        ax.plot(
            qpoints,
            np.pi * np.abs(np.sin(qpoints / 2)),
            "--",
            color="red",
            lw=1.5,
            label="Upper bound",
        )
    ax.set_ylim(0, 3.6)
    ax.set_xlim(0, 2 * np.pi - 2 * np.pi / q_steps)
    ax.set_xlabel(r"$q$", fontsize=16)
    ax.set_ylabel(r"$\tilde{\omega} = \omega / J$", fontsize=16)
    ax.set_xticks([0, np.pi / 2, np.pi, 3 * np.pi / 2, 2 * np.pi])
    ax.set_xticklabels(["0", r"$\pi/2$", r"$\pi$", r"$3\pi/2$", r"$2\pi$"])
    if lower_bound or upper_bound:
        ax.legend(loc="upper right", fontsize=11)
    if title:
        ax.set_title(title, fontsize=14)
    plt.tight_layout()
    plt.show()


def plot_green(n, Gjjc, time_steps, dt, title=None):
    """Heat-map of the retarded Green's function in real space and time."""
    fig, ax = plt.subplots(figsize=(8, 6))
    t_axis = np.arange(1, time_steps + 1) * dt
    site_axis = np.arange(n)
    x, y = np.meshgrid(t_axis, site_axis)
    c = ax.pcolormesh(
        x,
        y,
        np.real(Gjjc).T,
        cmap="RdBu",
        vmax=0.5,
        vmin=-0.5,
        shading="auto",
    )
    fig.colorbar(c, ax=ax, label=r"Re $G^R(j, j_c, t)$")
    ax.set_xlabel(r"Time  ($t / J^{-1}$)", fontsize=16)
    ax.set_ylabel("Site index $j$", fontsize=16)
    if title:
        ax.set_title(title, fontsize=14)
    plt.tight_layout()
    plt.show()


#  Variational ground-state ansatz (HVA)


def _apply_xxz_pair_gate(qc, q0, q1, theta):
    """Apply the parameterized XXZ-type two-qubit gate used in the HVA."""
    qc.cx(q0, q1)
    qc.rz(theta, q1)
    qc.h(q0)
    qc.rz(theta + np.pi / 2, q0)
    qc.cx(q0, q1)
    qc.rz(-theta, q1)
    qc.h(q1)
    qc.cx(q1, q0)
    qc.rz(np.pi / 2, q1)
    qc.rz(-np.pi / 2, q0)
    qc.h(q1)
    qc.h(q0)


def build_gs_ansatz(n, params, layers):
    """Build the Hamiltonian variational ansatz (HVA) circuit for
    ground-state preparation of the 1D Heisenberg model.

    Starts from a product of singlet pairs and applies alternating
    odd/even layers of parameterized XXZ gates. For layer r,
    params[2 * r] is the odd-layer (inter-pair) angle and
    params[2 * r + 1] is the even-layer (intra-pair) angle.
    """
    qc = QuantumCircuit(n)
    # Initial singlet product state
    for i in range(n // 2):
        qc.x(2 * i)
        qc.x(2 * i + 1)
        qc.h(2 * i + 1)
        qc.cx(2 * i + 1, 2 * i)
    # Variational layers
    for r in range(layers):
        for i in range(1, (n + 1) // 2):  # odd layer
            _apply_xxz_pair_gate(qc, 2 * i - 1, 2 * i, params[2 * r])
        for i in range(n // 2):  # even layer
            _apply_xxz_pair_gate(qc, 2 * i, 2 * i + 1, params[2 * r + 1])
    return qc


def prepare_ground_state(n, gs_layers=5, max_bond=128, cutoff=1e-8):
    """Prepare the KCuF3 (isotropic Heisenberg) ground state as a QuantumCircuit.

    Runs DMRG (quimb MPO + DMRG2) to get the chain's ground state, then optimizes
    the HVA angles to maximize the MPS overlap |<psi_ansatz|psi_DMRG>|^2. No exact
    diagonalization, so it scales to larger n.
    """
    J = Jz = 1.0
    builder = qtn.SpinHam1D(S=1 / 2)
    builder += J * 0.5, "+", "-"
    builder += J * 0.5, "-", "+"
    builder += Jz, "Z", "Z"
    H_mpo = builder.build_mpo(L=n)
    dmrg = qtn.DMRG2(H_mpo)
    dmrg.solve(tol=1e-8, verbosity=0)

    gs_sim = QuimbSimulator(
        quimb_circuit_factory=partial(
            qtn.CircuitMPS, gate_opts=dict(cutoff=cutoff, max_bond=max_bond)
        ),
        autodiff_backend="jax",
    )

    def gs_infidelity(params):
        psi = tensornetwork_from_circuit(
            build_gs_ansatz(n, params, gs_layers), gs_sim
        ).psi
        return 1 - abs(psi.H @ dmrg.state) ** 2

    # Seed and optimizer match the original tutorial. Each layer starts at
    # [0, pi/2]: an odd-layer angle of 0 makes the inter-pair gate the identity,
    # and an even-layer angle of pi/2 makes the intra-pair gate a SWAP (since
    # 0.5 * (XX + YY + ZZ) = SWAP - I/2). That puts the seed at the singlet-pair
    # product limit, which is already a decent approximation to the Heisenberg
    # ground state, so the optimizer only has to refine it. The small jitter
    # (fixed RNG seed, so runs are reproducible) breaks the exact symmetry
    # between layers; COBYQA then runs for up to 100 iterations.
    rng = np.random.default_rng(12345)
    x0 = np.tile([0.0, np.pi / 2], gs_layers) + rng.normal(
        scale=0.1, size=2 * gs_layers
    )
    result_gs = scipy.optimize.minimize(
        gs_infidelity, x0, method="COBYQA", options={"maxiter": 100}
    )
    print(f"DMRG ground-state energy: {dmrg.energy:.6f}")
    print(f"GS fidelity: {1 - result_gs.fun:.4f}")
    return build_gs_ansatz(n, result_gs.x, gs_layers)


print("Setup complete - helpers defined.")

Output:

Setup complete - helpers defined.

関数テンプレートを読み込む

Qiskit Serverless に接続し、デプロイされた aqc-dynamics-function. を読み込みます。 このチュートリアルの両方の例では同じハンド fn ルを呼び出しているため、ここでは関数が一度だけ読み込まれます。

# Credentials are read from the account saved once via QiskitServerless.save_account(...)
serverless = QiskitServerless()
fn = serverless.load("aqc-dynamics-function")

小規模シミュレータの例

まず、exactバック statevector エンドを使用して、10店舗からなる小規模なチェーンでワークフロー全体を実行します。 これにより、QPUの処理時間を一切消費することなく、PRE → FUNCTION → POST のパイプラインの妥当性を確認できます。

ステップ1:古典的な入力を量子問題に写像する

KCuF3_3 のハミルトニアンを、各最近接結合において結合定数 14\tfrac14 を持つ( SparsePauliOp 等方性ハイゼンベルグ: XX+YY+ZZXX + YY + ZZ )として構築する。弦はパウリ演算子であるため、 14\tfrac14 によりスピン結合 12\frac{1}{2} が得られる。 DMRGとMPS-fidelity最大化を用いて基底状態を準備し、その後、中性子キック(中心サイトにおける π/2\pi/2 ZZ 回転)を組み込む。 準備した回路こそが、関数にとして渡すものです initial_state。 ここではデフォルト設定(サイトごとの ZZ )の observables ままにします。これは、中性子ワークフローに必要な ⟨σzj⟩(t)\langle \sigma_z^j \rangle(t) の読み取り値と完全に一致しています。

n = 10
dt = 0.6  # physical time per Trotter step (also the omega-axis unit in POST)
time_steps = 10
center = n // 2 - 1

# MPS-simulator settings, shared by the ground-state prep here and the AQC
# compression inside the function (matches the original tutorial).
mps_max_bond = 32
mps_cutoff = 1e-8

# 1D isotropic Heisenberg (KCuF3) Hamiltonian on n qubits
H = SparsePauliOp.from_sparse_list(
    [(p, [i, i + 1], 0.25) for i in range(n - 1) for p in ("XX", "YY", "ZZ")],
    num_qubits=n,
)

# Ground state (DMRG + fidelity max) + neutron kick baked into the same circuit
gs_circuit = prepare_ground_state(
    n, gs_layers=3, max_bond=mps_max_bond, cutoff=mps_cutoff
)
gs_circuit.rz(
    np.pi / 2, center
)  # exp(-i (pi/2)/2 Z_center): the neutron perturbation
print(
    f"Prepared {n}-qubit ground state with the neutron kick at site {center}."
)

Output:

DMRG ground-state energy: -4.258035
GS fidelity: 0.9841
Prepared 10-qubit ground state with the neutron kick at site 4.

手順 2 および 3:関数テンプレートを使用して圧縮および実行する

手作業によるワークフローでは、これらは2つの別々の段階、すなわちハードウェア向けに回路を最適化すること(ステップ2)と、それを実行すること(ステップ3)となります。 この関数テンプレートは、これら2つを1つの呼び出しにまとめます。 トロッター合成、AQC圧縮、ハードウェア向けトランスパイルを行い、その後、回路を実行します(ここではエクサクト・シミュレータ上で、後ほどはハードウェア上で組み込みのエラー緩和機能を用いて実行します)。 2つの調整パラメータは、 aqc_segments (圧縮プラン)と aqc_options (MPSおよびオプティマイザの設定)です。 各セグメントでは、連続 k するトロッターのステップを、- mステップのトロッターターゲットから構成されるアンザッツに圧縮 {"n_steps": k, "ansatz_steps": m} し、それを超えるステップは通常のトロッターとして実行 sum(n_steps) される。 初期の、エンタングルメントの低いステップは、浅い(ansatz_steps=1)アンザッツにうまく圧縮できるため、ここでは最初の3ステップを単層のアンザッツに、次の2ステップをより深い2層のアンザッツに圧縮する。10ステップあるトロッター法のうち、残りの5ステップは通常のトロッター法として実行される。 これは、元のチュートリアルと同様に、MPS結合寸法 max_bond=32、および反復回数の上限を100回に設定したL-BFGS-B最適化アルゴリズムを採用しているため aqc_options です cutoff=1e-8。

Setupで読み込まれた関数を呼び出してください。 backend="statevector" 正確な参照パスを実行します:QPU時間はかからず、回路はサーバーレスワーカー内の正確な状態ベクトルシミュレータ上で実行されます(呼び出すには、保存済みの Qiskit Serverless アカウントが依然として必要です)。 は、準備された基底状態(キックを含む)を initial_state 表します。は observables 省略されるため、この関数はデフォルトのサイトごとの ZZ を測定します。

job = fn.run(
    t_steps=time_steps,
    aqc_segments=[
        {
            "n_steps": 3,
            "ansatz_steps": 1,
        },  # early steps -> shallow 1-layer ansatz
        {
            "n_steps": 2,
            "ansatz_steps": 2,
        },  # later steps -> deeper 2-layer ansatz
    ],
    aqc_options={
        "max_bond": mps_max_bond,  # MPS bond dimension for AQC compression
        "cutoff": mps_cutoff,
        "optimizer_settings": {
            "method": "L-BFGS-B",
            "jac": True,
            "options": {"maxiter": 100},
        },
    },
    dt=dt,
    hamiltonian=H,
    initial_state=gs_circuit,  # prepared ground state including the neutron kick
    # observables omitted -> default per-site Z (the neutron sigma_z readout)
    backend="statevector",
)
print(job.status())  # rerun this cell until status says DONE

Output:

DONE
# The per-site <sigma_z>(t) the function returns is the retarded Green's function
# G(j, j_c, t). The workflow samples t = 1..time_steps, so drop the t = 0 row (the
# prepared+kicked state before any evolution) before post-processing.
result = job.result()
print(
    "AQC fidelities:",
    {k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

ev = np.array(result["expectation_values"])
Gjjc = ev[1:]  # shape (time_steps, n)
print("Green's function shape:", Gjjc.shape)

Output:

AQC fidelities: {'1': 1.0, '2': 0.9999, '3': 0.9992, '4': 0.9998, '5': 0.9995}
Green's function shape: (10, 10)

ステップ4:後処理を行い、結果を所望の従来の形式で出力する

グリーンの関数をフーリエ変換して S(q,ω)S(q, \omega) とし、鏡像対称化を行い、負の値を切り捨てる:これが中性子の標準的な後処理である。 このモデルでは、 S(q,ω)=S(−q,ω)S(q, \omega) = S(-q, \omega) となるため、ミラーリングは正確に行われます。また、残ってしまう負の値は、有限で離散的にサンプリングされた時系列をフーリエ変換した際に生じるアーティファクトであるため、これらはゼロに切り捨てられます。 この小規模な正確なシミュレーションでは、2スピノン連続体は粗くしか分解されていませんが、その仕組みは、後に続くハードウェア実行と全く同じです。

q_res, w_res = 100, 100
spectrum = get_spectrum(n, Gjjc, dt, time_steps, q_res, w_res)
spectrum = -(spectrum + spectrum[:, ::-1]) / 2  # mirror symmetry
spectrum = np.clip(spectrum, a_min=0, a_max=None)  # clip negatives

plot_green(
    n,
    Gjjc,
    time_steps,
    dt,
    title=f"Retarded Green's function - {n} qubits (AQC, statevector)",
)
plot_spectrum(
    spectrum,
    dt,
    q_res,
    w_res,
    lower_bound=True,
    upper_bound=True,
    title=f"Dynamical structure factor - {n} qubits (AQC, statevector)",
)

Output:

Output of the previous code cell Output of the previous code cell

大規模なハードウェアの例

このワークフローは、科学コードを一切変更することなくスケールアップが可能です。具体的には、30サイトのチェーン、トロッター深度の2倍(20ステップ)、アンザッツ深度を変化させる圧縮プラン(後方のより絡み合いの強いステップほど深いアンザッツを使用)、そして IBM Quantum プロセッサ上での実行に加え、関数に組み込まれたエラー軽減機能 (動的デカップリング、パウリ・トゥワーリング、およびトゥワーリング・リードアウト・エラー・エクスティンクション(TREX))を用いて実行されます。 シミュレータの例と同じ4つの手順を順を追って進め、Setupで取得したハンド fn ルを再利用します。

コラム「 1 」
小規模
大規模な
量子ビット1030時間まで
トロッター・ステップ1020
AQC圧縮されたステップ(1層+2層)3 + 2 = 56 + 4 = 10
基底状態のアンザッツ層35
MPSの最大結合寸法32128
バックエンドstatevectorDD、パウリ・トゥイール、TREXを搭載したQPU

ステップ1:古典的な入力を量子問題に写像する

同じ KCuF3_3ハイゼンベルグを構築し SparsePauliOp 、基底状態を準備する。この際、より長い鎖に対応するより深い gs_layers=5 アンザッツを用い、その後、中心サイトにおいて π/2\pi/2 ZZ の中性子キックを焼き込む。 これは小規模なマッピングと全く同じですが、 n=30n = 30 で行われます。

10サイトでのシミュレーションに比べて、基底状態の忠実度は低くなると予想されます。具体的には、ここでは 0.82 程度となるのに対し、より短い鎖の場合は 0.98 でした。これは、5層のHVAでは30サイトの基底状態を完全に捉えきれないためです。 これは失敗というよりは想定内のことであり、同じ理由から、元のチュートリアルでは50のサイトにおいて 0.65 程度が許容されています。 「Raising」または gs_layers COBYQAの反復回数上限を引き上げると、追加の古典的コストを伴いながらも、性能が向上します。

n = 30
dt = 0.6
time_steps = 20
center = n // 2 - 1

# Same MPS settings as the original large-scale run: a larger bond for the
# longer, more-entangled chain (shared by GS prep and AQC compression).
mps_max_bond = 128
mps_cutoff = 1e-8

# Same KCuF3 Hamiltonian and ground-state prep, on a larger chain
H = SparsePauliOp.from_sparse_list(
    [(p, [i, i + 1], 0.25) for i in range(n - 1) for p in ("XX", "YY", "ZZ")],
    num_qubits=n,
)
gs_circuit = prepare_ground_state(
    n, gs_layers=5, max_bond=mps_max_bond, cutoff=mps_cutoff
)
gs_circuit.rz(np.pi / 2, center)  # neutron kick at the center site
print(
    f"Prepared {n}-qubit ground state with the neutron kick at site {center}."
)

Output:

DMRG ground-state energy: -13.111355
GS fidelity: 0.8201
Prepared 30-qubit ground state with the neutron kick at site 14.

手順 2 および 3:関数テンプレートを使用して圧縮および実行する

シミュレータの例と同じ単一の呼び出しですが、今回は IBM Quantum プロセッサを指す backend_name ように設定されているため、関数はそこでトランスパイルされ、実行されます。 この圧縮計画では、アンザッツの深さを段階的に変化させています。最初の6ステップ(低エンタングルメント)のトロッター法は浅い単層アンザッツに圧縮され、次の4ステップはより深い2層アンザッツに圧縮され、20ステップのうち残りの10ステップは通常のトロッター法として実行されます。 aqc_options より長く、より絡み合った鎖については max_bond=128 、MPS結合次元を に引き上げ(元のものと同じ)、L-BFGS-B最適化アルゴリズムの反復回数上限を100回に据え置きます。 組み込みのエラー軽減機能(動的デカップリング( XY4 )、ゲート・トゥワーリング、およびTREX計測の軽減)を有効 estimator_options にします。 TREXの学習予算(measure_noise_learning)を除き、これらのすべてについて、関数のデフォルト値は元のチュートリアルとすでに一致しています。呼び出し元から指定された値は、関数のデフォルト値と統合されるのではなく、それらを全面的に上書き estimator_options してしまうため、ブロック全体が依然として記述されています。そのため、キーを省略した場合、関数のデフォルト値ではなく、 IBM Quantum Compute のデフォルト値が使用されることになります。

# Steps 2 + 3: the function compresses (varied ansatz) and executes on hardware.
job = fn.run(
    t_steps=time_steps,
    aqc_segments=[
        {
            "n_steps": 6,
            "ansatz_steps": 1,
        },  # early steps -> shallow 1-layer ansatz
        {
            "n_steps": 4,
            "ansatz_steps": 2,
        },  # later steps -> deeper 2-layer ansatz
    ],
    aqc_options={
        "max_bond": mps_max_bond,  # 128 for the longer chain
        "cutoff": mps_cutoff,
        "optimizer_settings": {
            "method": "L-BFGS-B",
            "jac": True,
            "options": {"maxiter": 100},
        },
    },
    dt=dt,
    hamiltonian=H,
    initial_state=gs_circuit,
    backend_name="ibm_pittsburgh",
    # Mitigation settings from the original tutorial. Only the two
    # measure_noise_learning values differ from the function's defaults; the rest
    # restates them, because a caller-supplied estimator_options dict replaces the
    # function's defaults wholesale rather than merging into them.
    estimator_options={
        "environment": {"job_tags": ["TUT-SNS"]},
        "dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
        "twirling": {
            "enable_gates": True,
            "num_randomizations": 1000,
            "shots_per_randomization": 128,
        },
        "resilience": {
            "measure_mitigation": True,
            "measure_noise_learning": {
                "num_randomizations": 32,
                "shots_per_randomization": 100,
            },
        },
    },
)
print("job ID (save this to reconnect later):", job.job_id)

Output:

job ID (save this to reconnect later): 43ed8d07-6d7d-4f33-b70a-7f31b765b310
長時間実行中のジョブへの再接続

大規模な実行には時間がかかり、その時間のほとんどはQPU上ではなく、従来型CPU上で処理されています。 AQC圧縮は、データがQPUに到達する前に、関数内部で実行されます。30カ所のサイトにおいて、この max_bond=128 処理には約4時間かかりましたが、このチュートリアルの冒頭にある「 使用時間の見積もり 」で示されているQPUの所要時間は約18分でした。 これら2つに加えて、キュー待ちが発生します。 このノートブックやカーネルは、実行中は開いたままにしておく必要はありません。

前のセルに表示されているジョブIDをコピーして、保存してください。 次の3つのセルでは、後で処理を再開することができます:

  1. 再接続(新しいカーネルセッションでのみ必要): セットアップセルを再実行して再作成し serverless、保存しておいたIDからハンド job ルを再構築してください。 送信したセッションがまだ有効な場合は、このセルをスキップしてください。ハンドルはすでに有効になっているためです。
  2. ステータスの確認:結果が表示されるまで再実行してください DONE。
  3. 結果を取得する:ステータスが になった場合にのみ実行する DONE。

次の再接続セルにはプレースホルダーが入っています。 これを自分のものに置き換えてください job_id:

# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Check where the job is. Re-run this until it reports DONE before fetching the
# result in the following cell: QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE ->
# RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING
# -> DONE.
print(job.status())

Output:

DONE
# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits (possibly for hours).
result = job.result()
print(
    "AQC fidelities:",
    {k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

ev = np.array(result["expectation_values"])
Gjjc = ev[1:]  # drop the t = 0 row -> shape (time_steps, n)

Output:

AQC fidelities: {'1': 1.0, '2': 0.9994, '3': 0.9944, '4': 0.9853, '5': 0.9747, '6': 0.959, '7': 0.9495, '8': 0.9542, '9': 0.9533, '10': 0.9451}

ステップ4:後処理を行い、結果を所望の従来の形式で出力する

シミュレータ実行時と同様の後処理を行う:グリーン関数をフーリエ変換して S(q,ω)S(q, \omega) とし、鏡像対称化を行い、負の値をクリップする。 鎖が長くなり、進化が進むにつれて、2スピノン連続体ははるかに鮮明に解明されるようになった。 破線の境界線の間の帯を埋め、 q=πq = \pi 付近で最も明るく見えるはずです。

n = result["metadata"]["n"]
q_res, w_res = 100, 100
spectrum = get_spectrum(n, Gjjc, dt, time_steps, q_res, w_res)
spectrum = -(spectrum + spectrum[:, ::-1]) / 2  # mirror symmetry
spectrum = np.clip(spectrum, a_min=0, a_max=None)  # clip negatives

plot_green(
    n,
    Gjjc,
    time_steps,
    dt,
    title=f"Retarded Green's function - {n} qubits (AQC, hardware)",
)
plot_spectrum(
    spectrum,
    dt,
    q_res,
    w_res,
    lower_bound=True,
    upper_bound=True,
    title=f"Dynamical structure factor - {n} qubits (AQC, hardware)",
)

Output:

Output of the previous code cell Output of the previous code cell

別紙

前述のハードウェアの例では、1つのチェーン長が実行されます。 以下に示す3つのスペクトルは、この同じワークフローを10、20、30の ibm_pittsburgh サイトで以前のハードウェア上で実行した結果であり、その他の入力パラメータはすべて固定されています。具体的には、20のトロッターステップ dt = 0.6、6つの1層および4つの2層のAQC圧縮ステップからなる圧縮プラン、および です max_bond = 128。 これらは記録された結果であり、前のセルからの出力ではありません。

3つのサイズすべてで同じ設定が使用されているため、スペクトルは直接比較可能です。 例えば、チェーンの長さに応じて調整したり、基底状態のアンザッツ層を増やしたり、の値を大きくしたりすることで max_bond、ここで示したどの結果よりも優れた結果が得られる可能性があります。

10サイトにおける動的構造因子。下限付近のq = πに、単一の鋭い明るいピークが見られる。10
キュービット
20サイトにおける動的構造因子。スペクトル重みは、2本の破線の2スピノン境界
の間の帯域を埋める。20キュービット
30サイトにおける動的構造因子。連続領域はより細かく分解されているが、コントラストは弱く、境界外にも一部の重みが割り当てられている。30
キュービット

次のステップ

推奨事項
  • このワークフローを自身のシステムに合わせて調整してください。この関数は、任意の 1D 近傍法を受け入れるため SparsePauliOp、異なる連鎖ハミルトニアン、初期状態、あるいは観測量セットであっても、PRE → FUNCTION → POSTという同じパイプラインで実行されます。 GitHub の「AQC Dynamics 」テンプレートで、入出力契約の全文をご確認ください。
  • このベンチマークの出典となった論文をご覧ください:Lee et al., 中性子散乱実験を用いた量子シミュレーションのベンチマーク ( arXiv:2603.15608 )。
  • 元の「中性子散乱のシミュレーション」 チュートリアルと比較すると、このインラインワークフローは、デプロイ済みの関数テンプレートに移植されたものです。
  • ハードウェア実行に適用されるエラー軽減および抑制手法、すなわち動的デカップリング、パウリ・トゥワーリング、TREXについてさらに詳しく見ていきましょう。
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。