Langkau ke kandungan utama

Model Ising Medan Melintang dengan Pengurusan Prestasi Q-CTRL

Anggaran penggunaan: 2 minit pada pemproses Heron r2. (NOTA: Ini adalah anggaran sahaja. Masa larian anda mungkin berbeza.)

Latar Belakang

Model Ising Medan Melintang (TFIM) penting untuk mengkaji kemagnetan kuantum dan peralihan fasa. Ia menerangkan set spin yang tersusun pada kekisi, di mana setiap spin berinteraksi dengan jirannya sambil turut dipengaruhi oleh medan magnet luaran yang mendorong turun naik kuantum.

Pendekatan lazim untuk mensimulasi model ini ialah menggunakan penguraian Trotter bagi menganggar operator evolusi masa, membina Circuit yang berselang-seli antara putaran qubit tunggal dan interaksi dua qubit yang menjalin kusutan. Namun, simulasi ini pada perkakasan sebenar adalah mencabar akibat hingar dan penyahkoherenan, yang menyebabkan penyelewengan daripada dinamik sebenar. Untuk mengatasinya, kami menggunakan alat penyekatan ralat dan pengurusan prestasi Fire Opal milik Q-CTRL, yang ditawarkan sebagai Fungsi Qiskit (lihat dokumentasi Fire Opal). Fire Opal secara automatik mengoptimumkan pelaksanaan Circuit dengan menerapkan gandingan dinamik, susun atur lanjutan, penghalaan, dan teknik penyekatan ralat lain — semua bertujuan mengurangkan hingar. Dengan penambahbaikan ini, keputusan perkakasan lebih hampir dengan simulasi tanpa hingar, maka kita boleh mengkaji dinamik kemagnetan TFIM dengan ketepatan lebih tinggi.

Dalam tutorial ini kita akan:

  • Membina Hamiltonian TFIM pada graf segitiga spin yang saling bersambung
  • Mensimulasi evolusi masa dengan Circuit Trotter pada kedalaman berbeza
  • Mengira dan menggambarkan kemagnetan qubit tunggal Zi\langle Z_i \rangle dari masa ke masa
  • Membandingkan simulasi garis dasar dengan keputusan larian perkakasan menggunakan pengurusan prestasi Fire Opal Q-CTRL

Gambaran Keseluruhan

Model Ising Medan Melintang (TFIM) ialah model spin kuantum yang merakam ciri-ciri penting peralihan fasa kuantum. Hamiltonian ditakrifkan sebagai:

H=JiZiZi+1hiXiH = -J \sum_{i} Z_i Z_{i+1} - h \sum_{i} X_i

di mana ZiZ_i dan XiX_i ialah operator Pauli yang bertindak pada Qubit ii, JJ ialah kekuatan gandingan antara spin jiran, dan hh ialah kekuatan medan magnet melintang. Sebutan pertama mewakili interaksi feromagnet klasik, manakala sebutan kedua memperkenalkan turun naik kuantum melalui medan melintang. Untuk mensimulasi dinamik TFIM, anda menggunakan penguraian Trotter bagi operator evolusi uniter eiHte^{-iHt}, dilaksanakan melalui lapisan Gate RX dan RZZ berdasarkan graf tersuai segitiga spin yang bersambung. Simulasi ini meneroka bagaimana kemagnetan Z\langle Z \rangle berkembang dengan peningkatan langkah Trotter.

Prestasi pelaksanaan TFIM yang dicadangkan dinilai dengan membandingkan simulasi tanpa hingar dengan Backend berisi hingar. Ciri pelaksanaan dipertingkat dan penyekatan ralat Fire Opal digunakan untuk mengurangkan kesan hingar dalam perkakasan sebenar, menghasilkan anggaran yang lebih dipercayai bagi pemerhatian spin seperti Zi\langle Z_i \rangle dan pemerangkap ZiZj\langle Z_i Z_j \rangle.

Keperluan

Sebelum memulakan tutorial ini, pastikan anda telah memasang yang berikut:

  • Qiskit SDK v1.4 atau lebih baharu, dengan sokongan visualization
  • Qiskit Runtime v0.40 atau lebih baharu (pip install qiskit-ibm-runtime)
  • Qiskit Functions Catalog v0.9.0 (pip install qiskit-ibm-catalog)
  • Fire Opal SDK v9.0.2 atau lebih baharu (pip install fire-opal)
  • Q-CTRL Visualizer v8.0.2 atau lebih baharu (pip install qctrl-visualizer)

Persediaan

Pertama, sahkan identiti menggunakan kunci API IBM Quantum anda. Kemudian, pilih Fungsi Qiskit seperti berikut. (Kod ini mengandaikan anda sudah menyimpan akaun anda ke persekitaran tempatan.)

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib networkx numpy qctrlvisualizer qiskit qiskit-aer qiskit-ibm-catalog qiskit-ibm-runtime
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.quantum_info import SparsePauliOp
from qiskit_aer import AerSimulator

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import qctrlvisualizer as qv
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Access Function
perf_mgmt = catalog.load("q-ctrl/performance-management")

Langkah 1: Petakan input klasik kepada masalah kuantum

Jana graf TFIM

Kita mulakan dengan mentakrifkan kekisi spin dan gandingan antara spin-spin tersebut. Dalam tutorial ini, kekisi dibina daripada segitiga yang bersambung dan disusun dalam rantai linear. Setiap segitiga terdiri daripada tiga nod yang bersambung dalam gelung tertutup, dan rantai dibentuk dengan menghubungkan satu nod setiap segitiga kepada segitiga sebelumnya.

Fungsi pembantu connected_triangles_adj_matrix membina matriks ketetanggaan bagi struktur ini. Untuk rantai nn segitiga, graf yang terhasil mengandungi 2n+12n+1 nod.

def connected_triangles_adj_matrix(n):
"""
Generate the adjacency matrix for 'n' connected triangles in a chain.
"""
num_nodes = 2 * n + 1
adj_matrix = np.zeros((num_nodes, num_nodes), dtype=int)

for i in range(n):
a, b, c = i * 2, i * 2 + 1, i * 2 + 2 # Nodes of the current triangle

# Connect the three nodes in a triangle
adj_matrix[a, b] = adj_matrix[b, a] = 1
adj_matrix[b, c] = adj_matrix[c, b] = 1
adj_matrix[a, c] = adj_matrix[c, a] = 1

# If not the first triangle, connect to the previous triangle
if i > 0:
adj_matrix[a, a - 1] = adj_matrix[a - 1, a] = 1

return adj_matrix

Untuk menggambarkan kekisi yang baru kita takrifkan, kita boleh plot rantai segitiga bersambung dan label setiap nod. Fungsi di bawah membina graf untuk bilangan segitiga yang dipilih dan memaparkannya.

def plot_triangle_chain(n, side=1.0):
"""
Plot a horizontal chain of n equilateral triangles.
Baseline: even nodes (0,2,4,...,2n) on y=0
Apexes: odd nodes (1,3,5,...,2n-1) above the midpoint.
"""
# Build graph
A = connected_triangles_adj_matrix(n)
G = nx.from_numpy_array(A)

h = np.sqrt(3) / 2 * side
pos = {}

# Place baseline nodes
for k in range(n + 1):
pos[2 * k] = (k * side, 0.0)

# Place apex nodes
for k in range(n):
x_left = pos[2 * k][0]
x_right = pos[2 * k + 2][0]
pos[2 * k + 1] = ((x_left + x_right) / 2, h)

# Draw
fig, ax = plt.subplots(figsize=(1.5 * n, 2.5))
nx.draw(
G,
pos,
ax=ax,
with_labels=True,
font_size=10,
font_color="white",
node_size=600,
node_color=qv.QCTRL_STYLE_COLORS[0],
edge_color="black",
width=2,
)
ax.set_aspect("equal")
ax.margins(0.2)
plt.show()

return G, pos

Untuk tutorial ini kita akan menggunakan rantai 20 segitiga.

n_triangles = 20
n_qubits = 2 * n_triangles + 1
plot_triangle_chain(n_triangles, side=1.0)
plt.show()

Output of the previous code cell

Mewarnakan tepi graf

Untuk melaksanakan gandingan spin–spin, adalah berguna untuk mengumpulkan tepi yang tidak bertindih. Ini membolehkan kita menerapkan Gate dua qubit secara selari. Kita boleh melakukan ini dengan prosedur pewarnaan tepi yang ringkas [1], yang menetapkan warna kepada setiap tepi supaya tepi yang bertemu pada nod yang sama diletakkan dalam kumpulan berbeza.

def edge_coloring(graph):
"""
Takes a NetworkX graph and returns a list of lists where each inner list contains
the edges assigned the same color.
"""
line_graph = nx.line_graph(graph)
edge_colors = nx.coloring.greedy_color(line_graph)

color_groups = {}
for edge, color in edge_colors.items():
if color not in color_groups:
color_groups[color] = []
color_groups[color].append(edge)

return list(color_groups.values())

Langkah 2: Optimumkan masalah untuk pelaksanaan perkakasan kuantum

Jana Circuit Trotter pada graf spin

Untuk mensimulasi dinamik TFIM, kita membina Circuit yang menganggar operator evolusi masa.

U(t)=eiHt,whereH=Ji,jZiZjhiXi.U(t) = e^{-i H t}, \quad \text{where} \quad H = -J \sum_{\langle i,j \rangle} Z_i Z_j - h \sum_i X_i .

Kita menggunakan penguraian Trotter peringkat kedua:

eiHΔteiHXΔt/2eiHZΔteiHXΔt/2,e^{-i H \Delta t} \approx e^{-i H_X \Delta t / 2}\, e^{-i H_Z \Delta t}\, e^{-i H_X \Delta t / 2},

di mana HX=hiXiH_X = -h \sum_i X_i dan HZ=Ji,jZiZjH_Z = -J \sum_{\langle i,j \rangle} Z_i Z_j.

  • Sebutan HXH_X dilaksanakan dengan lapisan putaran RX.
  • Sebutan HZH_Z dilaksanakan dengan lapisan Gate RZZ sepanjang tepi graf interaksi.

Sudut Gate-gate ini ditentukan oleh medan melintang hh, pemalar gandingan JJ, dan langkah masa Δt\Delta t. Dengan menindih beberapa langkah Trotter, kita menjana Circuit dengan kedalaman yang semakin meningkat bagi menganggar dinamik sistem. Fungsi generate_tfim_circ_custom_graph dan trotter_circuits membina Circuit kuantum Trotter daripada graf interaksi spin sewenang-wenangnya.

def generate_tfim_circ_custom_graph(
steps, h, J, dt, psi0, graph: nx.graph.Graph, meas_basis="Z", mirror=False
):
"""
Generate a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2) for simulating a transverse field ising model:
e^{-i H t} where the Hamiltonian H = -J \\sum_i Z_i Z_{i+1} + h \\sum_i X_i.

steps: Number of trotter steps
theta_x: Angle for layer of X rotations
theta_zz: Angle for layer of ZZ rotations
theta_x: Angle for second layer of X rotations
J: Coupling between nearest neighbor spins
h: The transverse magnetic field strength
dt: t/total_steps
psi0: initial state (assumed to be prepared in the computational basis).
meas_basis: basis to measure all correlators in

This is a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2)
"""
theta_x = h * dt
theta_zz = -2 * J * dt
nq = graph.number_of_nodes()
color_edges = edge_coloring(graph)
circ = QuantumCircuit(nq, nq)
# Initial state, for typical cases in the computational basis
for i, b in enumerate(psi0):
if b == "1":
circ.x(i)
# Trotter steps
for step in range(steps):
for i in range(nq):
circ.rx(theta_x, i)
if mirror:
color_edges = [sublist[::-1] for sublist in color_edges[::-1]]
for edge_list in color_edges:
for edge in edge_list:
circ.rzz(theta_zz, edge[0], edge[1])
for i in range(nq):
circ.rx(theta_x, i)

# some typically used basis rotations
if meas_basis == "X":
for b in range(nq):
circ.h(b)
elif meas_basis == "Y":
for b in range(nq):
circ.sdg(b)
circ.h(b)

for i in range(nq):
circ.measure(i, i)

return circ

def trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, mirror=True):
"""
Generates a sequence of Trotterized circuits, each with increasing depth.
Given a spin interaction graph and Hamiltonian parameters, it constructs
a list of circuits with 1 to d_ind_tot Trotter steps

G: Graph defining spin interactions (edges = ZZ couplings)
d_ind_tot: Number of Trotter steps (maximum depth)
J: Coupling between nearest neighboring spins
h: Transverse magnetic field strength
dt: (t / total_steps
meas_basis: Basis to measure all correlators in
mirror: If True, mirror the Trotter layers
"""
qubit_count = len(G)
circuits = []
psi0 = "0" * qubit_count

for steps in range(1, d_ind_tot + 1):
circuits.append(
generate_tfim_circ_custom_graph(
steps, h, J, dt, psi0, G, meas_basis, mirror
)
)
return circuits

Anggar kemagnetan qubit tunggal Zi\langle Z_i \rangle

Untuk mengkaji dinamik model, kita ingin mengukur kemagnetan setiap Qubit, yang ditakrifkan oleh nilai jangkaan Zi=ψZiψ\langle Z_i \rangle = \langle \psi | Z_i | \psi \rangle.

Dalam simulasi, kita boleh mengira ini terus daripada hasil pengukuran. Fungsi z_expectation memproses kiraan bitstring dan mengembalikan nilai Zi\langle Z_i \rangle bagi indeks Qubit yang dipilih. Pada perkakasan sebenar, kita menilai kuantiti yang sama dengan menentukan operator Pauli menggunakan fungsi generate_z_observables, kemudian Backend mengira nilai jangkaan tersebut.

def z_expectation(counts, index):
"""
counts: Dict of mitigated bitstrings.
index: Index i in the single operator expectation value < II...Z_i...I > to be calculated.
return: < Z_i >
"""
z_exp = 0
tot = 0
for bitstring, value in counts.items():
bit = int(bitstring[index])
sign = 1
if bit % 2 == 1:
sign = -1
z_exp += sign * value
tot += value

return z_exp / tot
def generate_z_observables(nq):
observables = []
for i in range(nq):
pauli_string = "".join(["Z" if j == i else "I" for j in range(nq)])
observables.append(SparsePauliOp(pauli_string))
return observables
observables = generate_z_observables(n_qubits)

Kita kini takrifkan parameter untuk menjana Circuit Trotter. Dalam tutorial ini, kekisi ialah rantai 20 segitiga bersambung, yang bersamaan sistem 41-Qubit.

all_circs_mirror = []
for num_triangles in [n_triangles]:
for meas_basis in ["Z"]:
A = connected_triangles_adj_matrix(num_triangles)
G = nx.from_numpy_array(A)
nq = len(G)
d_ind_tot = 22
dt = 2 * np.pi * 1 / 30 * 0.25
J = 1
h = -7
all_circs_mirror.extend(
trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, True)
)
circs = all_circs_mirror

Langkah 3: Laksanakan menggunakan primitif Qiskit

Jalankan simulasi MPS

Senarai Circuit Trotter dilaksanakan menggunakan simulator matrix_product_state dengan pilihan sewenang-wenang sebanyak 40964096 tembakan. Kaedah MPS memberikan anggaran cekap bagi dinamik Circuit, dengan ketepatan yang ditentukan oleh dimensi ikatan yang dipilih. Bagi saiz sistem yang dipertimbangkan di sini, dimensi ikatan lalai sudah cukup untuk menangkap dinamik kemagnetan dengan ketepatan tinggi. Kiraan mentah dinormalkan, dan daripada situ kita mengira nilai jangkaan qubit tunggal Zi\langle Z_i \rangle pada setiap langkah Trotter. Akhirnya, kita mengira purata ke atas semua Qubit untuk mendapatkan satu lengkung tunggal yang menunjukkan perubahan kemagnetan dari masa ke masa.

backend_sim = AerSimulator(method="matrix_product_state")

def normalize_counts(counts_list, shots):
new_counts_list = []
for counts in counts_list:
a = {k: v / shots for k, v in counts.items()}
new_counts_list.append(a)
return new_counts_list

def run_sim(circ_list):
shots = 4096
res = backend_sim.run(circ_list, shots=shots)
normed = normalize_counts(res.result().get_counts(), shots)
return normed

sim_counts = run_sim(circs)

Jalankan pada perkakasan

service = QiskitRuntimeService()
backend = service.backend("ibm_marrakesh")

def run_qiskit(circ_list):
shots = 4096
pm = generate_preset_pass_manager(backend=backend)
isa_circuits = [pm.run(qc) for qc in circ_list]
sampler = Sampler(mode=backend)
res = sampler.run(isa_circuits, shots=shots)
res = [r.data.c.get_counts() for r in res.result()]
normed = normalize_counts(res, shots)
return normed

qiskit_counts = run_qiskit(circs)

Jalankan pada perkakasan dengan Fire Opal

Kita menilai dinamik kemagnetan pada perkakasan kuantum sebenar. Fire Opal menyediakan Fungsi Qiskit yang melanjutkan primitif Estimator Qiskit Runtime standard dengan penyekatan ralat automatik dan pengurusan prestasi. Kita menghantar Circuit Trotter terus ke Backend IBM® manakala Fire Opal mengendalikan pelaksanaan yang peka terhadap hingar.

Kita menyediakan senarai pubs, di mana setiap item mengandungi Circuit dan pemerhatian Pauli-Z yang sepadan. Ini dihantar ke fungsi Estimator Fire Opal, yang mengembalikan nilai jangkaan Zi\langle Z_i \rangle bagi setiap Qubit pada setiap langkah Trotter. Keputusan tersebut kemudiannya boleh dipuratakan ke atas Qubit untuk mendapatkan lengkung kemagnetan daripada perkakasan.

backend_name = "ibm_marrakesh"
estimator_pubs = [(qc, observables) for qc in all_circs_mirror[:]]

# Run the circuit using the estimator
qctrl_estimator_job = perf_mgmt.run(
primitive="estimator",
pubs=estimator_pubs,
backend_name=backend_name,
options={"default_shots": 4096},
)

result_qctrl = qctrl_estimator_job.result()

Langkah 4: Proses selepas dan kembalikan keputusan dalam format klasik yang diingini

Akhirnya, kita membandingkan lengkung kemagnetan daripada simulator dengan keputusan yang diperoleh pada perkakasan sebenar. Memplot kedua-duanya secara berdampingan menunjukkan betapa rapatnya pelaksanaan perkakasan dengan Fire Opal mencocoki garis dasar tanpa hingar merentasi langkah Trotter.

def make_correlators(test_counts, nq, d_ind_tot):
mz = np.empty((nq, d_ind_tot))
for d_ind in range(d_ind_tot):
counts = test_counts[d_ind]
for i in range(nq):
mz[i, d_ind] = z_expectation(counts, i)
average_z = np.mean(mz, axis=0)
return np.concatenate((np.array([1]), average_z), axis=0)

sim_exp = make_correlators(sim_counts[0:22], nq=nq, d_ind_tot=22)
qiskit_exp = make_correlators(qiskit_counts[0:22], nq=nq, d_ind_tot=22)
qctrl_exp = [ev.data.evs for ev in result_qctrl[:]]
qctrl_exp_mean = np.concatenate(
(np.array([1]), np.mean(qctrl_exp, axis=1)), axis=0
)
def make_expectations_plot(
sim_z,
depths,
exp_qctrl=None,
exp_qctrl_error=None,
exp_qiskit=None,
exp_qiskit_error=None,
plot_from=0,
plot_upto=23,
):
import numpy as np
import matplotlib.pyplot as plt

depth_ticks = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

d = np.asarray(depths)[plot_from:plot_upto]
sim = np.asarray(sim_z)[plot_from:plot_upto]

qk = (
None
if exp_qiskit is None
else np.asarray(exp_qiskit)[plot_from:plot_upto]
)
qc = (
None
if exp_qctrl is None
else np.asarray(exp_qctrl)[plot_from:plot_upto]
)

qk_err = (
None
if exp_qiskit_error is None
else np.asarray(exp_qiskit_error)[plot_from:plot_upto]
)
qc_err = (
None
if exp_qctrl_error is None
else np.asarray(exp_qctrl_error)[plot_from:plot_upto]
)

# ---- helper(s) ----
def rmse(a, b):
if a is None or b is None:
return None
a = np.asarray(a, dtype=float)
b = np.asarray(b, dtype=float)
mask = np.isfinite(a) & np.isfinite(b)
if not np.any(mask):
return None
diff = a[mask] - b[mask]
return float(np.sqrt(np.mean(diff**2)))

def plot_panel(ax, method_y, method_err, color, label, band_color=None):
# Noiseless reference
ax.plot(d, sim, color="grey", label="Noiseless simulation")

# Method line + band
if method_y is not None:
ax.plot(d, method_y, color=color, label=label)
if method_err is not None:
lo = np.clip(method_y - method_err, -1.05, 1.05)
hi = np.clip(method_y + method_err, -1.05, 1.05)
ax.fill_between(
d,
lo,
hi,
alpha=0.18,
color=band_color if band_color else color,
label=f"{label} ± error",
)
else:
ax.text(
0.5,
0.5,
"No data",
transform=ax.transAxes,
ha="center",
va="center",
fontsize=10,
color="0.4",
)

# RMSE box (vs sim)
r = rmse(method_y, sim)
if r is not None:
ax.text(
0.98,
0.02,
f"RMSE: {r:.4f}",
transform=ax.transAxes,
va="bottom",
ha="right",
fontsize=8,
bbox=dict(
boxstyle="round,pad=0.35", fc="white", ec="0.7", alpha=0.9
),
)
# Axes
ax.set_xticks(depth_ticks)
ax.set_ylim(-1.05, 1.05)
ax.grid(True, which="both", linewidth=0.4, alpha=0.4)
ax.set_axisbelow(True)
ax.legend(prop={"size": 8}, loc="best")

fig, axes = plt.subplots(1, 2, figsize=(10, 4), dpi=300, sharey=True)

axes[0].set_title("Fire Opal (Q-CTRL)", fontsize=10)
plot_panel(
axes[0],
qc,
qc_err,
color="#680CE9",
label="Fire Opal",
band_color="#680CE9",
)
axes[0].set_xlabel("Trotter step")
axes[0].set_ylabel(r"$\langle Z \rangle$")
axes[1].set_title("Qiskit", fontsize=10)
plot_panel(
axes[1], qk, qk_err, color="blue", label="Qiskit", band_color="blue"
)
axes[1].set_xlabel("Trotter step")

plt.tight_layout()
plt.show()
depths = list(range(d_ind_tot + 1))
errors = np.abs(np.array(qctrl_exp_mean) - np.array(sim_exp))

errors_qiskit = np.abs(np.array(qiskit_exp) - np.array(sim_exp))
make_expectations_plot(
sim_exp,
depths,
exp_qctrl=qctrl_exp_mean,
exp_qctrl_error=errors,
exp_qiskit=qiskit_exp,
exp_qiskit_error=errors_qiskit,
)

Output of the previous code cell

Rujukan

[1] Graph coloring. Wikipedia. Retrieved September 15, 2025, from https://en.wikipedia.org/wiki/Graph_coloring

Tinjauan tutorial

Sila luangkan masa sebentar untuk memberikan maklum balas tentang tutorial ini. Pandangan anda akan membantu kami menambah baik kandungan dan pengalaman pengguna.

Pautan ke tinjauan

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.

Source: IBM Quantum docs — updated 15 Jan 2026
English version on doQumentation — updated 7 Mei 2026
This translation based on the English version of 9 Apr 2026