I-simulate ang mga noisy na quantum system gamit ang Pauli propagation
Sa gabay na ito, gagamitin natin ang pauli-prop package para i-simulate nang classical ang time dynamics ng isang noisy na nine-qubit na transverse-field Ising model (TFIM) sa 3x3 square lattice. Gumagamit tayo ng mga instruction na PauliLindbladError para tukuyin ang isang noise channel, , na kumikilos sa isang set ng mga entangling layer, . Pagkatapos, ipo-propagate natin ang observable, , pabalik sa noisy na circuit at tatantiyahin ang mga expectation value para sa iba't ibang noise model, pati na rin sa noiseless na kaso.

Habang pina-propagate pabalik ang observable sa circuit, ang bawat noise channel, , na nauugnay sa entangling layer, , ay nagpapahina sa mga Pauli term sa na nag-a-anti-commute sa mga Pauli-Lindblad generator nito. Partikular, kung ang ay isang Pauli generator ng na may rate, , ang isang Pauli term, , sa ay nagbabago bilang: , kung saan ang ay ang coefficient ng . Kapag na-propagate na ang sa simula ng circuit, ang expectation value kaugnay ng zero state, , ay madaling makalkula sa pamamagitan ng pagsasama-sama ng mga coefficient ng bawat diagonal na term sa (mga term na naglalaman ng o sa lahat ng qubit).
Workflow:
- Tukuyin ang TFIM lattice, at gumamit ng edge coloring para tukuyin ang pinakamaliit na set ng mga entangling layer
- Gumawa ng mga synthetic na noise model, , para sa bawat natatanging entangling layer,
- Gumawa ng mga noise model na may iba't ibang scale para pag-aralan ang epekto ng gate noise sa sistema
- Gumawa ng mga noiseless at noisy na quantum circuit para sa iba't ibang depth at noise scale na pinag-aaralan
- Sa mga noisy na circuit, ipinapasok ang mga instruction na
PauliLindbladErrorbago ang bawat entangling layer
- Sa mga noisy na circuit, ipinapasok ang mga instruction na
- Gumamit ng Pauli propagation para i-simulate ang mga eksaktong expectation value ng sistema sa iba't ibang depth
- Para sa siyam na qubit, ginagawa ito sa pamamagitan ng pagpapalaki sa hanggang na term, na sumasaklaw sa buong Pauli space
- Gumamit ng Pauli propagation para i-simulate ang mga noisy na expectation value
- Obserbahan kung paano sinisira ng pagtaas ng gate noise ang katumpakan ng quantum model
Gumawa ng 3x3 square lattice at maghanap ng 4-coloring sa mga edge
Ang mga vertex sa graph ay kumakatawan sa mga qubit, at ang mga edge ay kumakatawan sa koneksyon sa pagitan ng dalawang qubit. Ang edge coloring ay tumutugma sa mga natatanging entangling layer sa quantum circuit kung saan ang mga gate sa mga koneksyong may magkakaibang kulay ay hindi maaaring ilapat nang sabay.
Madalas na mahalaga ang pagtukoy ng pinakamaliit na set ng mga natatanging entangling layer para sa pagpapatupad ng mga episyenteng noise-learning protocol, dahil kailangang matutunan nang hiwalay ang noise ng bawat layer. Habang dumarami ang mga layer na kailangan nating matutunan, dumarami rin ang mga shot na kailangan nating kunin mula sa QPU. Para sa demo na ito, ginagamit natin ang impormasyon ng layer para bumuo ng mga noisy na circuit at magpasok ng mga instruction na PauliLindbladError mula sa qiskit-aer bago ang bawat entangling layer para imodelo ang gate noise ng QPU.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "pauli_prop": "pauli-prop", "qiskit": "qiskit", "qiskit_addon_utils": "qiskit-addon-utils", "qiskit_aer": "qiskit-aer", "rustworkx": "rustworkx"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from collections import defaultdict
import numpy as np
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.coloring import auto_color_edges
# Define rectangular square-lattice on 20 qubits
num_rows = 3
num_cols = 3
num_qubits = num_rows * num_cols
coupling_map = CouplingMap.from_grid(
num_rows=num_rows, num_columns=num_cols, bidirectional=False
)
# Create mapping from color to edge list
coloring = auto_color_edges(coupling_map.get_edges())
color_to_edge = defaultdict(list)
for edge, color in coloring.items():
color_to_edge[color].append(edge)
from rustworkx import PyDiGraph
from rustworkx.visualization import graphviz_draw
# Inspect graph coupling and unique entangling layers
print(
f"The circuit will have {num_qubits} qubits and {len(color_to_edge)} unique entangling layers."
)
sq_lattice = PyDiGraph()
sq_lattice.extend_from_weighted_edge_list(
[
(source, target, color)
for ((source, target), color) in coloring.items()
]
)
def color_edge_4color(edge):
color_dict = {0: "red", 1: "green", 2: "blue", 3: "orange"}
return {"color": color_dict[edge]}
graphviz_draw(sq_lattice, edge_attr_fn=color_edge_4color, method="neato")
The circuit will have 9 qubits and 4 unique entangling layers.

Gumawa ng mga synthetic na noise model
Bago gumawa ng mga quantum circuit, gagawa tayo ng noise model (instance ng PauliLindbladError) para sa bawat entangling layer. Isasama natin ang mga ito bilang mga instruction sa ating mga quantum circuit mamaya. Para sa bawat layer, gagawa tayo ng mga noise channel na may iba't ibang scale. Partikular, gagawa tayo ng mga noise model na may Error Per Layered Gate (EPLG) na humigit-kumulang .0004, .0008, .0012, .0016, at .002.
from qiskit.quantum_info import SparsePauliOp, pauli_basis
from qiskit_aer.noise import PauliLindbladError
# Pauli-Lindblad noise parameters
seed = 1764
target_EPLGs = [0.0004, 0.0008, 0.0012, 0.0016, 0.002]
def generate_random_pauli_lindblad_noise(
edges,
num_qubits: int | None = None,
noise_scale: float = 1e-3,
seed: int | None = None,
) -> PauliLindbladError:
"""Generate random Pauli-Lindblad noise over the full Pauli basis."""
if num_qubits is None:
num_qubits = np.max(edges)
basis_paulis = [p for p in pauli_basis(2) if np.sum(p.x + p.z)]
basis_paulis = SparsePauliOp.from_sparse_list(
[
(pauli.to_label(), edge, 1)
for pauli in basis_paulis
for edge in edges
],
num_qubits=num_qubits,
)
basis_paulis = basis_paulis.simplify()
basis_paulis = basis_paulis.paulis
rng = np.random.default_rng(seed=seed)
rates = rng.random(len(basis_paulis)) * noise_scale
return PauliLindbladError(generators=basis_paulis, rates=rates)
num_generators = (
(num_rows * num_cols)
+ (num_rows - 1) * num_cols
+ num_rows * (num_cols - 1)
)
noise_scales = [
EPLG * (num_rows * num_cols) / num_generators for EPLG in target_EPLGs
]
noise_models_per_EPLG = [
[
generate_random_pauli_lindblad_noise(
color_to_edge[color],
num_qubits=num_qubits,
noise_scale=noise_scale,
seed=seed,
)
for color in range(len(color_to_edge))
]
for noise_scale in noise_scales
]
Gawin ang mga quantum circuit
Para sa demo na ito, sini-simulate natin ang time dynamics ng isang transverse-field Ising model (TFIM) para sa dumaraming bilang ng mga Trotter step (1-10 step). Para sa bawat isa sa 10 circuit depth, sini-simulate natin ang epekto ng gate noise, gamit ang mga noise model na may iba't ibang scale (EPLGs = .0004, .0008, .0012, .0016, .002). Ipinapasok ang noise sa QuantumCircuit bilang instruction na PauliLindbladError mula sa Qiskit Aer. Ang Hamiltonian na isinasaalang-alang ay:
kung saan ang ay naglalarawan ng coupling ng mga magkatabing spin, , at ang ay ang global na transverse field.
Dito, ipinapatupad natin ang time-evolved na Hamiltonian sa iba't ibang time at noise scale. Gumagawa tayo ng 60 kabuuang circuit: 10 noiseless na circuit na magkakaiba ang Trotter depth, at 50 noisy na circuit para sa 10 Trotter depth sa limang noise scale. Dahil sa isang connectivity graph, ang model ay pina-parametrize ng ilang variable:
num_steps: Ang bilang ng mga Trotter stepJ: Lakas ng coupling ng mga konektadong siteh: Lakas ng external na magnetic fielddt: Pagbabago ng oras sa bawat Trotter stepinitial_state_angle: Isang initial na excitation, , na ilalapat nang pantay sa lahat ng qubit
from typing import Any
from qiskit import QuantumCircuit
# Ising model parameters
num_steps = 10
J = -1.0
dt = 0.25 / abs(J)
h = 2.0 * abs(J)
initial_state_angle = np.pi / 18.0
rx_angle = 2.0 * h * dt
rzz_angle = 2.0 * J * dt
def generate_ising_circuit(
num_qubits: int,
num_steps: int,
rx_angle: float,
rzz_angle: float,
coloring: dict[Any, list[tuple[int, int]]],
layer_noise_models: list[PauliLindbladError] | None = None,
initial_state_angle: float | None = None,
) -> QuantumCircuit:
"""Generate a quantum circuit implementing a transverse-field Ising model"""
qc = QuantumCircuit(num_qubits)
if initial_state_angle:
qc.ry(initial_state_angle, range(num_qubits))
qc.rx(rx_angle / 2, range(num_qubits))
for i in range(num_steps):
for j, layer in enumerate(coloring):
edges = coloring[layer]
if layer_noise_models:
qc.append(layer_noise_models[j], qargs=range(num_qubits))
for edge in edges:
qc.rzz(rzz_angle, *edge)
if i == num_steps - 1:
qc.rx(rx_angle / 2, range(num_qubits))
else:
qc.rx(rx_angle, range(num_qubits))
return qc
# Create the noiseless and noisy circuits
noiseless_circs = []
noisy_circs = []
for steps in range(1, num_steps + 1):
noiseless_circs.append(
generate_ising_circuit(
num_qubits,
steps,
rx_angle,
rzz_angle,
color_to_edge,
initial_state_angle=initial_state_angle,
)
)
noisy_circs_per_step = []
for noise_models in noise_models_per_EPLG:
noisy_circs_per_step.append(
generate_ising_circuit(
num_qubits,
steps,
rx_angle,
rzz_angle,
color_to_edge,
layer_noise_models=noise_models,
initial_state_angle=initial_state_angle,
)
)
noisy_circs.append(noisy_circs_per_step)
print(
f"{num_steps} noiseless and {num_steps * len(target_EPLGs)} noisy Trotter circuits generated. {num_steps} different depths across {len(target_EPLGs)} different noise models"
)
print("\nBelow: Initial state and one noisy Trotter step.")
noisy_circs[0][0].draw("mpl", fold=-1)
10 noiseless and 50 noisy Trotter circuits generated. 10 different depths across 5 different noise models
Below: Initial state and one noisy Trotter step.

Tukuyin ang observable at patakbuhin ang mga simulation
Para sa demo na ito, sini-simulate natin ang mga expectation value ng average na two-site correlator:
kung saan ang ay tumutugma sa uniform na rotation sa lahat ng qubit, ang ay naglalarawan ng Trotter layer, at ang ay nag-i-index sa lahat ng konektadong pares ng vertex sa lattice.
Panghuli, gagamitin natin ang pauli_prop para i-simulate ang mga expectation value ng observable para sa bawat circuit. Para sa nine-qubit na demo na ito, ginagawa natin ang lahat ng simulation nang eksakto. Walang gagawing Pauli propagation truncation; kaya, ang mga pagkakaiba sa mga expectation value sa iba't ibang noise model ay buong maiuugnay sa gate error. Ang proseso ng simulation ay hinahawakan sa apat na hakbang:
- I-evolve ang mga Clifford gate sa circuit papunta sa harap ng circuit gamit ang
pauli_prop.evolve_through_cliffords - I-propagate ang observable sa non-Clifford na bahagi ng circuit gamit ang
pauli_prop.propagate_through_circuit- Gumagawa tayo ng mga eksaktong simulation sa pamamagitan ng pagpapalaki sa observable hanggang sa laki ng buong Pauli space,
- I-propagate ang evolved na observable sa Clifford na bahagi ng circuit gamit ang
SparsePauliOp.evolveng Qiskit - Tantiyahin ang expectation value kaugnay ng zero state, , sa pamamagitan ng pagsasama-sama ng mga coefficient ng bawat diagonal na term sa (mga term na naglalaman ng o sa lahat ng qubit)
import time
from pauli_prop import evolve_through_cliffords, propagate_through_circuit
from qiskit.quantum_info import Pauli
# Average ZZ-correlator observable
id_pauli = Pauli("I" * num_qubits)
observable = 2 * SparsePauliOp(
[
id_pauli.dot(Pauli("ZZ"), [i, j])
for i in range(num_qubits)
for j in range(i + 1, num_qubits)
]
)
observable /= num_qubits**2
# Pauli propagation parameters
max_terms = 4**num_qubits # Exact propagation
atol = 1e-12
# Run simulations
exact_evs = []
noisy_evs = [[] for _ in range(len(target_EPLGs))]
st = time.perf_counter()
for i, noiseless_circ in enumerate(noiseless_circs):
cliff, non_cliff = evolve_through_cliffords(noiseless_circ)
evolved_obs = propagate_through_circuit(
observable, non_cliff, max_terms=max_terms, atol=atol, frame="h"
)[0]
evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame="h")
exact_evs.append(
float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())
)
for j in range(len(target_EPLGs)):
noisy_circ = noisy_circs[i][j]
cliff, non_cliff = evolve_through_cliffords(noisy_circ)
evolved_obs = propagate_through_circuit(
observable, non_cliff, max_terms=max_terms, atol=1e-12, frame="h"
)[0]
evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame="h")
noisy_evs[j].append(
float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())
)
print(
f"Ran {len(noiseless_circs)} noiseless and {len(target_EPLGs) * num_steps} noisy simulations in {int(time.perf_counter() - st)}s."
)
Ran 10 noiseless and 50 noisy simulations in 103s.
Obserbahan ang epekto ng gate error sa model
Tandaan na, dahil nine-qubit na eksperimento ito, eksakto ang Pauli propagation routine, at ang lahat ng error sa mga noise plot ay maiuugnay sa gate error.
import matplotlib.pyplot as plt
xs = range(1, num_steps + 1)
plt.plot(xs, exact_evs, label="Noiseless", color="black", marker="o")
colors = [".3", ".4", ".5", ".6", ".7"]
for i, evs in enumerate(noisy_evs):
plt.plot(
xs,
evs,
label=f"{target_EPLGs[i]} EPLG",
linestyle="--",
color=colors[i],
marker="o",
)
plt.xlabel("# Trotter steps")
plt.ylabel(r"$\langle Z_{tot}^2 \rangle$")
plt.legend()
plt.show()
