Simulasi serakan neutron dengan aliran kerja Serverless dinamik AQC + Trotter
Anggaran penggunaan: 18 minit pada pemproses Heron r3 (NOTA: Ini hanyalah anggaran. Masa larian anda mungkin berbeza.)
Hasil pembelajaran
-
Bagaimana spektrum serakan neutron tidak elastik memetakan kepada faktor struktur dinamik bagi magnet kuantum 1D.
-
Cara menyediakan keadaan asas KCuF (Heisenberg isotropik) dengan kumpulan penormalan matriks ketumpatan (DMRG) dan pemaksimuman kesetiaan keadaan matriks produk (MPS).
-
Cara menjalankan evolusi masa Trotter, pemampatan litar pengkompilan kuantum anggaran (AQC), dan pelaksanaan yang dikurangkan sebagai satu panggilan fungsi sahaja.
-
Cara memproses pasca siri masa setiap tapak ke dalam dan mengenal pasti kontinum dua-spinon.
Prasyarat
-
Kebiasaan dengan corak Qiskit,
SparsePauliOp, dan evolusi masa Trotter. -
Pendedahan asas kepada kaedah rangkaian tensor (DMRG dan MPS) adalah berguna tetapi tidak diperlukan, begitu juga kebiasaan dengan pustaka
qiskit-addon-aqc-tensoryang digunakan oleh fungsi untuk memampatkan litar Trotter.
Latar belakang
Serakan neutron tidak elastik mengukur faktor struktur dinamik , transformasi Fourier ruang-dan-masa bagi fungsi korelasi spin-spin, jadi menghasilkan semula daripada model spin mikroskopik adalah ujian langsung dan boleh dipalsukan bagi simulasi kuantum. Tutorial ini mengkaji KCuF, rantai Heisenberg antiferromagnetik spin- yang pengujaannya bukan pembalikan spin tunggal tetapi pasangan spinon terpecahan: bukannya penyebaran magnon yang tajam, menunjukkan kontinum dua-spinon yang luas, dibatasi di bawah oleh dan di atas oleh . Itulah lengkung bertitik pada plot yang berikut. Fizik secara penuh, dan perbandingan terhadap data neutron yang diukur, dibincangkan dalam tutorial asal dan dalam Lee et al., arXiv:2603.15608.
Aliran kerja kuantum ini mencerminkan eksperimen serakan:
-
Sediakan keadaan asas rantai .
-
Ketuk dengan gangguan setempat pada tapak tengah, putaran- , meniru pemindahan momentum dan tenaga daripada neutron.
-
Evolusikan masa di bawah Hamiltonian Heisenberg, , dengan formula produk Trotter.
-
Ukur kemagnetan per-tapak . Sebagai fungsi tapak dan masa , ini adalah tepat fungsi Green tertahan , jadi tiada penukaran diperlukan sebelum transformasi Fourier dalam langkah 5.
-
Transformasi Fourier kepada .
Masalah boleh timbul dalam langkah 3, apabila litar Trotter tepat untuk evolusi panjang menjadi terlalu dalam untuk perkakasan. AQC dengan rangkaian tensor menangani ini dengan memampatkan satu blok langkah Trotter ke dalam ansatz berparameter yang tetap dan cetek yang kesetiaan keadaannya kepada evolusi tepat dimaksimumkan secara klasik dengan simulator MPS (arXiv:2301.08609). Templat Dinamik AQC membungkus keseluruhan teras kuantum ini (sintesis Trotter, pemampatan AQC, dan pelaksanaan yang dikurangkan) di sebalik satu panggilan:
| PRE (buku nota ini) | FUNGSI (aqc-dynamics-function) | POST (buku nota ini) |
|---|---|---|
| Keadaan asas daripada DMRG ditambah pemaksimuman kesetiaan-MPS, dengan ketukan neutron dibina terus dalam litar yang sama | Sintesis Trotter → pemampatan AQC → pelaksanaan pada statevector, fake, atau runtime, mengembalikan per tapak | , faktor struktur dinamik |
Kerja khusus eksperimen kekal di sini dalam buku nota: penyediaan keadaan asas (PRE) dan pemprosesan pasca (POST). Kedua-dua langkah berat-kuantum, pemampatan dan pelaksanaan, berjalan di dalam fungsi.
Tutorial ini adalah rakan kepada Simulasi serakan neutron dalam bahan kuantum dengan litar kuantum, yang membina eksperimen yang sama secara sebaris: model KCuF yang sama, penyediaan keadaan asas, ketukan neutron, dan pemprosesan pasca, dengan sintesis Trotter, pemampatan AQC, dan pelaksanaan yang dikurangkan ditulis langkah demi langkah. Baca tutorial itu untuk mempelajari cara pemampatan AQC berfungsi. Baca yang ini untuk menjalankan eksperimen yang sama melalui templat fungsi yang digunakan: teras kuantum menjadi satu panggilan fungsi sahaja, dan pemampatan AQC berjam-jam berjalan di dalam pekerja Serverless dan bukannya pada mesin anda, jadi anda tidak memerlukan sistem HPC atau kernel terbuka semasa ia berjalan. Panggilan yang sama juga menggerakkan eksperimen dinamik 1D yang lain.
Keperluan
Sebelum memulakan tutorial ini, pastikan anda mempunyai perkara berikut:
-
Fungsi yang digunakan pada akaun Qiskit Serverless anda. Jalankan templat fungsi rakan dahulu: Gunakan dan jalankan templat fungsi dinamik AQC + Trotter. Panduan itu menerangkan cara mendapatkan fail sumber dan memuat naik fungsi ke akaun anda. Tutorial ini hanya memanggil fungsi yang telah digunakan.
-
Kelayakan IBM Quantum® disimpan untuk
QiskitServerless(lihat templat fungsi). Kedua-dua contoh dalam tutorial ini memanggil fungsi yang digunakan, jadi kedua-duanya memerlukannya. -
Qiskit SDK v2.0 atau lebih baharu (
pip install qiskit). -
Klien Qiskit IBM Catalog (
pip install qiskit-ibm-catalog). -
NumPy, SciPy, dan Matplotlib (
pip install numpy scipy matplotlib). SciPy 1.14 atau lebih baharu diperlukan untuk pengoptimum COBYQA yang digunakan dalam penyediaan keadaan asas. -
Tindanan rangkaian tensor AQC, kerana penyediaan keadaan asas dalam Langkah 1 berjalan secara setempat dalam buku nota ini:
pip install 'qiskit-addon-aqc-tensor[quimb-jax]==0.3.1'.
Panggilan pertama kepada fungsi yang baru digunakan menunggu semasa pekerja Serverless memasang kebergantungannya, jadi jangkakan kependaman tambahan pada larian itu.
Persediaan
Import pustaka dan takrifkan pembantu khusus eksperimen yang digunakan kemudian: build_gs_ansatz (ansatz variasi Hamiltonian, atau HVA, untuk penyediaan keadaan asas), prepare_ground_state (DMRG ditambah pemaksimuman kesetiaan-MPS), dan get_spectrum, plot_green, dan plot_spectrum (pemprosesan pasca ). Ini disesuaikan daripada tutorial serakan neutron asal.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-aqc-tensor qiskit-ibm-catalog quimb scipy
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.")
Setup complete - helpers defined.
Muatkan templat fungsi
Sambung ke Qiskit Serverless dan muatkan aqc-dynamics-function yang telah digunakan. Kedua-dua contoh dalam tutorial ini memanggil pemegang fn yang sama, jadi fungsi tersebut dimuatkan sekali sahaja, di sini.
# Credentials are read from the account saved once via QiskitServerless.save_account(...)
serverless = QiskitServerless()
fn = serverless.load("aqc-dynamics-function")
Contoh simulator berskala kecil
Kami mula-mula menjalankan keseluruhan aliran kerja pada rantai 10-tapak yang kecil dengan menggunakan backend statevector yang tepat. Ini mengesahkan saluran paip PRE → FUNGSI → POST sebelum membelanjakan sebarang masa QPU.
Langkah 1: Memetakan input klasik kepada masalah kuantum
Bina Hamiltonian KCuF sebagai SparsePauliOp (Heisenberg isotropik: pada gandingan pada setiap ikatan jiran terdekat; rentetan-rentetan tersebut adalah operator Pauli, jadi memberikan gandingan spin-). Sediakan keadaan asas dengan DMRG serta pemaksimuman fideliti-MPS, kemudian masukkan tendangan neutron: putaran- pada tapak tengah. Litar yang disediakan itulah yang kita berikan kepada fungsi sebagai initial_state. Kita biarkan observables pada lalainya (per-tapak ), iaitu bacaan yang tepat diperlukan oleh aliran kerja neutron.
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}."
)
DMRG ground-state energy: -4.258035
GS fidelity: 0.9841
Prepared 10-qubit ground state with the neutron kick at site 4.
Langkah 2 dan 3: Mampatkan dan laksanakan dengan templat fungsi
Dalam aliran kerja yang ditulis secara manual, ini adalah dua peringkat berasingan: mengoptimumkan litar untuk perkakasan (Langkah 2) dan melaksanakannya (Langkah 3). Templat fungsi menggabungkan kedua-duanya menjadi satu panggilan. Ia melakukan sintesis Trotter, mampatan AQC, dan transpilasi perkakasan, kemudian menjalankan litar tersebut (di sini pada simulator tepat, kemudian dengan mitigasi ralat terbina-dalam pada perkakasan). Dua parameter penalaan ialah aqc_segments (pelan mampatan) dan aqc_options (tetapan MPS dan pengoptimum). Setiap segmen {"n_steps": k, "ansatz_steps": m} memampatkan k langkah Trotter berturutan menjadi ansatz yang dibina daripada sasaran Trotter m-langkah, dan mana-mana langkah melebihi sum(n_steps) dijalankan sebagai Trotter biasa. Langkah awal berkekusutan rendah dimampatkan dengan baik menjadi ansatz cetek (ansatz_steps=1), jadi di sini kita memampatkan tiga langkah pertama menjadi ansatz lapisan tunggal dan dua langkah seterusnya menjadi ansatz dua lapisan yang lebih dalam; baki lima daripada 10 langkah Trotter dijalankan sebagai Trotter biasa. Untuk aqc_options kita mencerminkan tutorial asal: dimensi ikatan MPS max_bond=32, cutoff=1e-8, dan pengoptimum L-BFGS-B dihadkan pada 100 lelaran.
Panggil fungsi yang dimuatkan dalam Setup. backend="statevector" menjalankan laluan rujukan yang tepat: tiada masa QPU, dengan litar dijalankan pada simulator statevector yang tepat di dalam pekerja serverless (akaun Qiskit Serverless yang disimpan tetap diperlukan untuk memanggilnya). initial_state membawa keadaan asas yang disediakan (termasuk tendangan); observables diabaikan supaya fungsi mengukur per-tapak lalai.
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
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)
AQC fidelities: {'1': 1.0, '2': 0.9999, '3': 0.9992, '4': 0.9998, '5': 0.9995}
Green's function shape: (10, 10)
Langkah 4: Pasca-proses dan kembalikan keputusan dalam format klasik yang dikehendaki
Jelmakan-Fourier fungsi Green ke dalam , cermin-simetrikan, dan potong nilai negatif: pasca-pemprosesan neutron piawai. Pencerminan adalah tepat kerana untuk model ini, dan nilai negatif yang tinggal adalah artifak daripada menjelmakan-Fourier siri masa yang terhingga dan disampel secara diskret, jadi ia dipotong kepada sifar. Pada larian tepat berskala kecil ini, kontinum dwi-spinon hanya diselesaikan secara kasar, tetapi mekanismenya adalah sama seperti larian perkakasan yang berikut.
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)",
)


Contoh perkakasan berskala besar
Aliran kerja yang sama diskalakan naik tanpa mengubah sebarang kod sains: rantai 30-tapak, dua kali kedalaman Trotter (20 langkah), pelan mampatan yang mengubah kedalaman ansatz (ansatz yang lebih dalam untuk langkah-langkah kemudian yang lebih berkekusutan), dan pelaksanaan pada pemproses IBM Quantum dengan mitigasi ralat terbina-dalam fungsi (penyahgandingan dinamik, twirling Pauli, dan pemadaman ralat bacaan berpintal (TREX)). Kita melalui empat langkah yang sama seperti contoh simulator, menggunakan semula pengendali fn daripada Setup.
| Skala kecil | Skala besar | |
|---|---|---|
| Qubit | 10 | 30 |
| Langkah Trotter | 10 | 20 |
| Langkah dimampat-AQC (1-lapisan + 2-lapisan) | 3 + 2 = 5 | 6 + 4 = 10 |
| Lapisan ansatz keadaan-asas | 3 | 5 |
| Dimensi ikatan maksimum MPS | 32 | 128 |
| Backend | statevector | QPU dengan DD, twirling Pauli, dan TREX |
Langkah 1: Memetakan input klasik kepada masalah kuantum
Bina SparsePauliOp Heisenberg KCuF yang sama dan sediakan keadaan asas, kini dengan ansatz gs_layers=5 yang lebih dalam untuk rantai yang lebih panjang, kemudian masukkan tendangan neutron pada tapak tengah. Ini adalah serupa dengan pemetaan skala kecil, tetapi pada .
Jangkakan fideliti keadaan-asas yang lebih rendah berbanding larian 10-tapak: sekitar 0.82 di sini berbanding 0.98 untuk rantai yang lebih kecil, kerana lima lapisan HVA tidak dapat menangkap sepenuhnya keadaan asas 30-tapak. Itu dijangka dan bukan satu kegagalan, dan tutorial asal menerima kira-kira 0.65 pada 50 tapak atas sebab yang sama. Menaikkan gs_layers atau had lelaran COBYQA meningkatkannya, dengan kos klasik tambahan.
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}."
)
DMRG ground-state energy: -13.111355
GS fidelity: 0.8201
Prepared 30-qubit ground state with the neutron kick at site 14.
Langkah 2 dan 3: Mampatkan dan laksanakan dengan templat fungsi
Panggilan tunggal yang sama seperti contoh simulator, kini dengan backend_name menunjuk kepada pemproses IBM Quantum, jadi fungsi mentranspilasi dan melaksanakan di sana. Pelan mampatan mengubah kedalaman ansatz: enam langkah Trotter (berkekusutan rendah) pertama dimampatkan menjadi ansatz lapisan tunggal yang cetek, empat langkah seterusnya menjadi ansatz dua lapisan yang lebih dalam, dan baki 10 daripada 20 langkah dijalankan sebagai Trotter biasa. aqc_options menaikkan dimensi ikatan MPS kepada max_bond=128 untuk rantai yang lebih panjang dan lebih berkekusutan (memadankan yang asal), mengekalkan pengoptimum L-BFGS-B yang sama dihadkan pada 100 lelaran. estimator_options menghidupkan mitigasi ralat terbina-dalam: penyahgandingan dinamik (XY4), twirling get, dan mitigasi pengukuran TREX. Lalai fungsi sudah memadankan tutorial asal untuk semua ini kecuali bajet pembelajaran TREX (measure_noise_learning). Keseluruhan blok tetap ditulis kerana estimator_options yang dibekalkan oleh pemanggil menggantikan lalai fungsi secara keseluruhan dan bukannya bercantum dengannya, jadi mengabaikan satu kunci akan kembali kepada lalai IBM Quantum Compute dan bukannya lalai fungsi.
# 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)
job ID (save this to reconnect later): 43ed8d07-6d7d-4f33-b70a-7f31b765b310
Larian berskala besar tidak pantas, dan kebanyakan masanya adalah klasik dan bukannya pada QPU. Mampatan AQC berjalan di dalam fungsi sebelum apa-apa mencapai QPU: pada 30 tapak dengan max_bond=128 ini mengambil masa hampir empat jam dalam larian kita, berbanding kira-kira 18 minit masa QPU yang dinyatakan dalam Anggaran penggunaan di bahagian atas tutorial ini. Menunggu giliran adalah tambahan kepada kedua-duanya. Anda tidak perlu mengekalkan notebook atau kernel ini terbuka semasa ia berjalan.
Salin ID tugas yang dicetak oleh sel sebelumnya dan simpan ia. Tiga sel seterusnya membolehkan anda menyambung semula kemudian:
-
Sambung semula, hanya diperlukan dalam sesi kernel baharu: jalankan semula sel Setup untuk mencipta semula
serverless, kemudian bina semula pengendalijobdaripada ID yang anda simpan. Langkau sel ini jika anda masih berada dalam sesi tempat anda menghantar, kerana pengendali itu sudah aktif. -
Semak status: jalankan semula sehingga ia melaporkan
DONE. -
Dapatkan keputusan: jalankan hanya sebaik status adalah
DONE.
Sel sambung-semula yang berikut mengandungi ruang letak. Gantikan ia dengan job_id anda sendiri:
# 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())
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)
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}
Langkah 4: Pasca-proses dan kembalikan keputusan dalam format klasik yang dikehendaki
Pasca-pemprosesan yang sama seperti larian simulator: jelmakan-Fourier fungsi Green ke dalam , cermin-simetrikan, dan potong nilai negatif. Dengan rantai dan evolusi yang lebih panjang, kontinum dwi-spinon jauh lebih baik diselesaikan. Ia sepatutnya mengisi jalur antara batas bergaris putus, paling terang berhampiran .
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)",
)


Lampiran
Contoh perkakasan sebelumnya menjalankan panjang rantai tunggal. Tiga spektrum berikut berasal daripada larian perkakasan terdahulu bagi aliran kerja yang sama ini pada ibm_pittsburgh pada 10, 20, dan 30 tapak, dengan setiap input lain dikekalkan tetap: 20 langkah Trotter pada dt = 0.6, pelan mampatan enam langkah AQC satu-lapisan tambah empat langkah AQC dua-lapisan, dan max_bond = 128. Ini adalah keputusan yang direkodkan, bukan output daripada sel-sel sebelumnya.
Tetapan yang sama digunakan pada ketiga-tiga saiz, jadi spektrum-spektrum tersebut boleh dibandingkan secara langsung. Menala mengikut panjang rantai, dengan lebih banyak lapisan ansatz keadaan-asas atau max_bond yang lebih besar, sebagai contoh, boleh memberikan keputusan yang lebih baik daripada yang ditunjukkan di sini.



Langkah seterusnya
-
Sesuaikan aliran kerja ini kepada sistem anda sendiri: fungsi ini menerima mana-mana
SparsePauliOpjiran terdekat 1D, jadi Hamiltonian rantai, keadaan awal, atau set observables yang berbeza menjalankan saluran paip PRE → FUNCTION → POST yang sama. Lihat kontrak input/output penuh dalam AQC Dynamics Template di GitHub. -
Baca kertas kerja asal penanda aras ini: Lee et al., Benchmarking quantum simulation with neutron-scattering experiments (arXiv:2603.15608).
-
Bandingkan dengan tutorial asal "Simulate neutron scattering", aliran kerja sebaris yang mana tutorial ini alihkan ke templat fungsi yang digunakan.
-
Selami dengan lebih mendalam teknik mitigasi dan penindasan ralat yang digunakan pada larian perkakasan: penyahgandingan dinamik, twirling Pauli, dan TREX.