Lewati ke konten utama

Simulasikan hamburan neutron dengan Serverless workflow dinamika AQC + Trotter

Perkiraan penggunaan: 18 menit pada prosesor Heron r3 (CATATAN: Ini hanya perkiraan. Runtime kamu bisa berbeda.)

Hasil pembelajaran

  • Bagaimana spektrum hamburan neutron inelastis dipetakan ke faktor struktur dinamis S(q,ω)S(q, \omega) dari magnet kuantum 1D.

  • Bagaimana menyiapkan keadaan dasar KCuF3_3 (isotropic Heisenberg) dengan density matrix renormalization group (DMRG) dan maksimasi fidelitas matrix product state (MPS).

  • Bagaimana menjalankan evolusi waktu Trotter, kompresi sirkuit approximate quantum compilation (AQC), dan eksekusi dengan mitigasi sebagai satu panggilan fungsi.

  • Bagaimana melakukan post-process deret waktu σz(t)\langle \sigma_z \rangle(t) per situs menjadi S(q,ω)S(q, \omega) dan mengidentifikasi kontinum two-spinon.

Prasyarat

Latar Belakang

Hamburan neutron inelastis mengukur faktor struktur dinamis S(q,ω)S(q, \omega), transformasi Fourier ruang-dan-waktu dari fungsi korelasi spin-spin, jadi mereproduksi S(q,ω)S(q, \omega) dari model spin mikroskopis adalah uji langsung yang dapat difalsifikasi dari simulasi kuantum. Tutorial ini mempelajari KCuF3_3, rantai Heisenberg antiferomagnetik spin-12\frac{1}{2} yang eksitasinya bukan berupa spin flip tunggal melainkan pasangan spinon terfraksionasi: alih-alih dispersi magnon yang tajam, S(q,ω)S(q, \omega) menunjukkan kontinum two-spinon yang lebar, dibatasi di bawah oleh π2sinq\tfrac{\pi}{2}|\sin q| dan di atas oleh πsin(q/2)\pi|\sin(q/2)|. Itulah kurva putus-putus pada plot yang mengikuti. Fisika secara lengkap, dan perbandingan dengan data neutron yang terukur, dibahas dalam tutorial asli dan dalam Lee et al., arXiv:2603.15608.

Alur kerja kuantum ini mencerminkan eksperimen hamburan:

  1. Siapkan keadaan dasar rantai ψ0|\psi_0\rangle.

  2. Berikan gangguan lokal pada situs tengah, rotasi ZZ sebesar π/2\pi/2, meniru transfer momentum dan energi dari neutron.

  3. Evolusikan waktu di bawah Hamiltonian Heisenberg, eiHte^{-iHt}, dengan formula produk Trotter.

  4. Ukur magnetisasi per-situs σzj(t)\langle \sigma_z^j \rangle(t). Sebagai fungsi dari situs jj dan waktu tt, ini persis fungsi Green tertunda (retarded) GR(j,jc,t)G^R(j, j_c, t), jadi tidak ada konversi yang diperlukan sebelum transformasi Fourier di langkah 5.

  5. Transformasikan Fourier GRG^R menjadi S(q,ω)S(q, \omega).

Masalah dapat muncul pada langkah 3, ketika sirkuit Trotter eksak untuk evolusi panjang menjadi terlalu dalam untuk perangkat keras. AQC dengan tensor networks mengatasi ini dengan mengompresi blok langkah Trotter menjadi ansatz terparameterisasi yang tetap dan dangkal yang fidelitas keadaannya terhadap evolusi eksak dimaksimalkan secara klasik dengan simulator MPS (arXiv:2301.08609). AQC Dynamics Template mengemas seluruh inti kuantum ini (sintesis Trotter, kompresi AQC, dan eksekusi dengan mitigasi) di balik satu panggilan:

PRE (notebook ini)FUNCTION (aqc-dynamics-function)POST (notebook ini)
Keadaan dasar dari DMRG ditambah maksimasi fidelitas MPS, dengan kick neutron disisipkan langsung ke dalam sirkuit yang samaSintesis Trotter → kompresi AQC → eksekusi pada statevector, fake, atau runtime, mengembalikan σzj(t)\langle \sigma_z^j \rangle(t) per situsS(q,ω)S(q, \omega), faktor struktur dinamis

Pekerjaan yang spesifik pada eksperimen tetap ada di sini di dalam notebook: penyiapan keadaan dasar (PRE) dan post-processing S(q,ω)S(q, \omega) (POST). Kedua langkah yang berat secara kuantum, kompresi dan eksekusi, berjalan di dalam fungsi.

Tutorial ini merupakan pendamping dari Simulate neutron scattering in quantum materials with quantum circuits, yang membangun eksperimen yang sama secara inline: model KCuF3_3 yang sama, penyiapan keadaan dasar, kick neutron, dan post-processing, dengan sintesis Trotter, kompresi AQC, dan eksekusi dengan mitigasi ditulis langkah demi langkah. Baca tutorial itu untuk mempelajari bagaimana kompresi AQC bekerja. Baca yang ini untuk menjalankan eksperimen yang sama melalui function template yang di-deploy: inti kuantumnya menjadi satu panggilan fungsi, dan kompresi AQC yang memakan waktu berjam-jam berjalan di dalam Serverless worker alih-alih di mesin kamu, sehingga kamu tidak memerlukan sistem HPC atau kernel yang tetap terbuka selama berjalan. Panggilan yang sama juga menjalankan eksperimen dinamika 1D lainnya.

Persyaratan

Sebelum memulai tutorial ini, pastikan kamu memiliki hal berikut:

  • Fungsi yang sudah di-deploy ke akun Qiskit Serverless kamu. Jalankan dulu function template pendamping: Deploy and run the AQC + Trotter dynamics function template. Panduan itu menjelaskan cara mendapatkan berkas sumber dan mengunggah fungsi ke akun kamu. Tutorial ini hanya memanggil fungsi yang sudah di-deploy.

  • Kredensial IBM Quantum® yang disimpan untuk QiskitServerless (lihat function template). Kedua contoh dalam tutorial ini memanggil fungsi yang di-deploy, jadi keduanya memerlukannya.

  • Qiskit SDK v2.0 atau yang lebih baru (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 yang lebih baru dibutuhkan untuk optimizer COBYQA yang digunakan dalam penyiapan keadaan dasar.

  • Tumpukan tensor-network AQC, karena penyiapan keadaan dasar di Langkah 1 berjalan secara lokal di notebook ini: pip install 'qiskit-addon-aqc-tensor[quimb-jax]==0.3.1'.

Panggilan pertama ke fungsi yang baru di-deploy akan menunggu sementara Serverless worker menginstal dependensinya, jadi perkirakan ada latensi ekstra pada eksekusi tersebut.

Setup

Impor library dan definisikan helper khusus eksperimen yang digunakan nanti: build_gs_ansatz (ansatz variasional Hamiltonian, atau HVA, untuk penyiapan keadaan dasar), prepare_ground_state (DMRG ditambah maksimasi fidelitas MPS), dan get_spectrum, plot_green, serta plot_spectrum (post-processing S(q,ω)S(q, \omega)). Ini diadaptasi dari tutorial hamburan neutron asli.

# 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.

Muat templat fungsi

Hubungkan ke Qiskit Serverless dan muat aqc-dynamics-function yang sudah di-deploy. Kedua contoh dalam tutorial ini memanggil handle fn yang sama, jadi fungsi ini dimuat sekali, di sini.

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

Contoh simulator skala kecil

Pertama kita menjalankan seluruh alur kerja pada rantai kecil 10-situs dengan menggunakan backend statevector yang eksak. Ini memvalidasi pipeline PRE → FUNCTION → POST sebelum menghabiskan waktu QPU sama sekali.

Langkah 1: Memetakan input klasik ke masalah kuantum

Bangun Hamiltonian KCuF3_3 sebagai SparsePauliOp (Heisenberg isotropik: XX+YY+ZZXX + YY + ZZ pada kopling 14\tfrac14 di setiap ikatan tetangga terdekat; string tersebut adalah operator Pauli, jadi 14\tfrac14 memberikan kopling spin-12\frac{1}{2}). Siapkan keadaan dasar dengan DMRG ditambah maksimasi fidelitas MPS, lalu sisipkan kick neutron langsung ke dalam sirkuit: rotasi ZZ sebesar π/2\pi/2 di situs tengah. Sirkuit yang telah disiapkan ini adalah yang kita serahkan ke fungsi sebagai initial_state. Kita membiarkan observables pada nilai defaultnya (per-situs ZZ), yang persis merupakan pembacaan σzj(t)\langle \sigma_z^j \rangle(t) yang dibutuhkan alur 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: Kompres dan eksekusi dengan templat fungsi

Dalam alur kerja yang ditulis manual, ini adalah dua tahap terpisah: mengoptimalkan sirkuit untuk perangkat keras (Langkah 2) dan mengeksekusinya (Langkah 3). Function template menggabungkan keduanya menjadi satu panggilan. Ia melakukan sintesis Trotter, kompresi AQC, dan transpilasi perangkat keras, lalu menjalankan sirkuit (di sini pada simulator eksak, nanti dengan mitigasi kesalahan bawaan pada perangkat keras). Dua parameter penyetelan adalah aqc_segments (rencana kompresi) dan aqc_options (pengaturan MPS dan optimizer). Setiap segmen {"n_steps": k, "ansatz_steps": m} mengompresi k langkah Trotter berurutan menjadi ansatz yang dibangun dari target Trotter m-langkah, dan setiap langkah di luar sum(n_steps) berjalan sebagai Trotter biasa. Langkah-langkah awal dengan entanglement rendah mengompres dengan baik ke dalam ansatz dangkal (ansatz_steps=1), jadi di sini kita mengompresi tiga langkah pertama ke dalam ansatz satu-lapis dan dua langkah berikutnya ke dalam ansatz dua-lapis yang lebih dalam; sisa lima dari 10 langkah Trotter berjalan sebagai Trotter biasa. Untuk aqc_options kita mencerminkan tutorial asli: dimensi bond MPS max_bond=32, cutoff=1e-8, dan optimizer L-BFGS-B yang dibatasi pada 100 iterasi.

Panggil fungsi yang dimuat di Setup. backend="statevector" menjalankan jalur referensi eksak: tanpa waktu QPU, dengan sirkuit berjalan pada simulator statevector eksak di dalam serverless worker (akun Qiskit Serverless yang sudah disimpan tetap diperlukan untuk memanggilnya). initial_state membawa keadaan dasar yang telah disiapkan (termasuk kick-nya); observables dihilangkan sehingga fungsi mengukur ZZ per-situs default.

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: Pascaproses dan kembalikan hasil dalam format klasik yang diinginkan

Transformasikan Fourier fungsi Green menjadi S(q,ω)S(q, \omega), simetrikan cermin, dan potong nilai negatif: post-processing neutron standar. Pencerminan bersifat eksak karena S(q,ω)=S(q,ω)S(q, \omega) = S(-q, \omega) untuk model ini, dan nilai negatif yang tersisa adalah artefak dari transformasi Fourier deret waktu yang berhingga dan disampel secara diskrit, sehingga dipotong menjadi nol. Pada eksekusi eksak kecil ini kontinum two-spinon hanya teresolusi secara kasar, tetapi mekanismenya identik dengan eksekusi perangkat keras yang mengikuti.

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 of the previous code cell

Output of the previous code cell

Contoh perangkat keras skala besar

Alur kerja yang sama diskalakan tanpa mengubah kode ilmiah apa pun: rantai 30-situs, dua kali kedalaman Trotter (20 langkah), rencana kompresi yang memvariasikan kedalaman ansatz (ansatz lebih dalam untuk langkah-langkah berikutnya yang lebih terbelit/entangled), dan eksekusi pada prosesor IBM Quantum dengan mitigasi kesalahan bawaan dari fungsi (dynamical decoupling, Pauli twirling, dan twirled readout error extinction (TREX)). Kita melalui empat langkah yang sama seperti contoh simulator, menggunakan kembali handle fn dari Setup.

Skala kecilSkala besar
Qubits1030
Langkah Trotter1020
Langkah terkompresi AQC (1-lapis + 2-lapis)3 + 2 = 56 + 4 = 10
Lapisan ansatz keadaan dasar35
Dimensi ikatan maksimum MPS32128
BackendstatevectorQPU dengan DD, Pauli twirling, dan TREX

Langkah 1: Memetakan input klasik ke masalah kuantum

Bangun SparsePauliOp Heisenberg KCuF3_3 yang sama dan siapkan keadaan dasar, sekarang dengan ansatz gs_layers=5 yang lebih dalam untuk rantai yang lebih panjang, lalu sisipkan kick neutron ZZ sebesar π/2\pi/2 di situs tengah langsung ke dalam sirkuit. Ini identik dengan pemetaan skala-kecil, tetapi pada n=30n = 30.

Perkirakan fidelitas keadaan dasar yang lebih rendah dibandingkan eksekusi 10-situs: sekitar 0.82 di sini terhadap 0.98 untuk rantai yang lebih kecil, karena lima lapisan HVA tidak dapat sepenuhnya menangkap keadaan dasar 30-situs. Ini yang diharapkan bukan kegagalan, dan tutorial asli menerima sekitar 0.65 pada 50 situs karena alasan yang sama. Menaikkan gs_layers atau batas iterasi COBYQA akan meningkatkannya, dengan biaya klasik ekstra.

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: Kompres dan eksekusi dengan templat fungsi

Panggilan tunggal yang sama seperti contoh simulator, sekarang dengan backend_name mengarah ke prosesor IBM Quantum, sehingga fungsi mentranspilasi dan mengeksekusi di sana. Rencana kompresi memvariasikan kedalaman ansatz: enam langkah Trotter pertama (entanglement rendah) dikompresi ke dalam ansatz dangkal satu-lapis, empat langkah berikutnya ke dalam ansatz dua-lapis yang lebih dalam, dan sisa 10 dari 20 langkah berjalan sebagai Trotter biasa. aqc_options menaikkan dimensi bond MPS menjadi max_bond=128 untuk rantai yang lebih panjang dengan entanglement lebih tinggi (sesuai dengan yang asli), tetap mempertahankan optimizer L-BFGS-B yang sama yang dibatasi pada 100 iterasi. estimator_options mengaktifkan mitigasi kesalahan bawaan: dynamical decoupling (XY4), gate twirling, dan mitigasi pengukuran TREX. Default fungsi sudah cocok dengan tutorial asli untuk semua ini kecuali anggaran pembelajaran TREX (measure_noise_learning). Seluruh blok tetap ditulis karena estimator_options yang diberikan pemanggil menggantikan default fungsi sepenuhnya alih-alih menggabungkannya, jadi menghilangkan sebuah kunci akan kembali ke default IBM Quantum Compute alih-alih default 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
Menyambung kembali ke job yang berjalan lama

Eksekusi skala-besar tidak cepat, dan sebagian besar waktunya bersifat klasik bukan pada QPU. Kompresi AQC berjalan di dalam fungsi sebelum apa pun mencapai QPU: pada 30 situs dengan max_bond=128 itu memakan waktu hampir empat jam pada eksekusi kita, dibandingkan dengan sekitar 18 menit waktu QPU yang disebutkan dalam Perkiraan penggunaan di bagian atas tutorial ini. Waktu tunggu antrean adalah tambahan dari keduanya. Kamu tidak perlu menjaga notebook atau kernel ini tetap terbuka selama berjalan.

Salin ID job yang dicetak oleh sel sebelumnya dan simpan. Tiga sel berikutnya memungkinkan kamu untuk melanjutkan pekerjaan nanti:

  1. Menyambung kembali, hanya diperlukan pada sesi kernel baru: jalankan ulang sel Setup untuk membuat ulang serverless, lalu bangun ulang handle job dari ID yang kamu simpan. Lewati sel ini jika kamu masih dalam sesi tempat kamu mengirimkan job, karena handle-nya sudah aktif.

  2. Periksa status: jalankan ulang sampai melaporkan DONE.

  3. Ambil hasil: jalankan hanya setelah status DONE.

Sel penyambungan-kembali berikut ini berisi placeholder. Ganti dengan job_id milikmu 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: Pascaproses dan kembalikan hasil dalam format klasik yang diinginkan

Post-processing yang identik dengan eksekusi simulator: Transformasikan Fourier fungsi Green menjadi S(q,ω)S(q, \omega), simetrikan cermin, dan potong nilai negatif. Dengan rantai dan evolusi yang lebih panjang, kontinum two-spinon jauh lebih baik teresolusi. Seharusnya mengisi band di antara batas putus-putus, paling terang di dekat 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 of the previous code cell

Output of the previous code cell

Lampiran

Contoh perangkat keras sebelumnya menjalankan satu panjang rantai. Tiga spektrum berikut berasal dari eksekusi perangkat keras sebelumnya dari alur kerja yang sama ini pada ibm_pittsburgh pada 10, 20, dan 30 situs, dengan semua input lain tetap konstan: 20 langkah Trotter pada dt = 0.6, rencana kompresi enam langkah satu-lapis ditambah empat langkah dua-lapis yang dikompresi AQC, dan max_bond = 128. Ini adalah hasil yang tercatat, bukan output dari sel-sel sebelumnya.

Pengaturan yang sama digunakan pada ketiga ukuran, jadi spektrum-spektrum tersebut dapat dibandingkan secara langsung. Menyetel per panjang rantai, dengan lebih banyak lapisan ansatz keadaan dasar atau max_bond yang lebih besar, misalnya, dapat memberikan hasil yang lebih baik daripada yang ditunjukkan di sini.

Dynamical structure factor at 10 sites, a single sharp bright peak at q = pi near the lower bound

Dynamical structure factor at 20 sites, spectral weight filling the band between the two dashed two-spinon bounds

Dynamical structure factor at 30 sites, the continuum resolved more finely with fainter contrast and some weight outside the bounds

Langkah selanjutnya

Rekomendasi