Змоделюй шумові квантові системи за допомогою Pauli propagation
У цьому посібнику ми використовуємо пакет pauli-prop, щоб класично змоделювати часову динаміку шумової дев'ятикубітної моделі Ізінга з поперечним полем (TFIM) на квадратній решітці 3x3. Ми використовуємо інструкції PauliLindbladError, щоб визначити канал шуму, , що діє на набір заплутувальних шарів, . Потім ми поширюємо спостережувану, , назад крізь шумову схему та оцінюємо математичні сподівання для різноманітних моделей шуму, а також для безшумового випадку.

Коли спостережувана поширюється крізь схему назад, кожен канал шуму, , пов'язаний із заплутувальним шаром, , пригнічує ті паулівські доданки в , які антикомутують із його генераторами Паулі–Ліндблада. Зокрема, якщо — паулівський генератор зі швидкістю, , то паулівський доданок, , в перетворюється так: , де — коефіцієнт при . Щойно поширено на початок схеми, математичне сподівання відносно нульового стану, , можна тривіально обчислити, підсумувавши коефіцієнти кожного діагонального доданка в (доданки, що містять або на всіх кубітах).
Робочий процес:
- Задай решітку TFIM і за допомогою розфарбовування ребер визнач мінімальний набір заплутувальних шарів
- Згенеруй синтетичні моделі шуму, , для кожного унікального заплутувального шару,
- Створи моделі шуму різних масштабів, щоб дослідити вплив шуму гейтів на систему
- Створи безшумові та шумові квантові схеми для різних глибин і масштабів шуму, що цікавлять
- У шумових схемах інструкції
PauliLindbladErrorвставляються перед кожним заплутувальним шаром
- У шумових схемах інструкції
- Використай Pauli propagation, щоб змоделювати точні математичні сподівання системи на різних глибинах
- Для дев'яти кубітів це робиться так: даємо зрости до доданків, що покриває весь паулівський простір
- Використай Pauli propagation, щоб змоделювати шумові математичні сподівання
- Спостерігай, як зростання шуму гейтів погіршує точність квантової моделі
Згенеруй квадратну решітку 3x3 і знайди 4-розфарбовування ребер
Вершини графа представляють кубіти, а ребра — зв'язок між двома кубітами. Розфарбовування ребер відповідає унікальним заплутувальним шарам у квантовій схемі так, що гейти на зв'язках різних кольорів не можуть застосовуватися одночасно.
Визначення мінімального набору унікальних заплутувальних шарів часто важливе для реалізації ефективних протоколів навчання шуму, оскільки шум кожного шару потрібно вивчати незалежно. Чим більше шарів ми маємо вивчити, тим більше шотів потрібно зробити на QPU. Для цієї демонстрації ми використовуємо інформацію про шари, щоб побудувати шумові схеми та вставити інструкції PauliLindbladError із qiskit-aer перед кожним заплутувальним шаром для моделювання шуму гейтів 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.

Згенеруй синтетичні моделі шуму
Перш ніж створювати квантові схеми, ми генеруємо модель шуму (екземпляр PauliLindbladError) для кожного із заплутувальних шарів. Пізніше ми вбудуємо їх як інструкції в наші квантові схеми. Для кожного шару ми генеруємо канали шуму різних масштабів. Зокрема, ми генеруємо моделі шуму з похибкою на шаровий гейт (Error Per Layered Gate, EPLG) приблизно .0004, .0008, .0012, .0016, і .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
]
Створи квантові схеми
Для цієї демонстрації ми моделюємо часову динаміку моделі Ізінга з поперечним полем (TFIM) для зростаючої кількості тротерівських кроків (1-10 кроків). Для кожної з 10 глибин схеми ми моделюємо вплив шуму гейтів за моделей шуму різних масштабів (EPLGs = .0004, .0008, .0012, .0016, .002). Шум вставляється в QuantumCircuit як інструкція PauliLindbladError із Qiskit Aer. Розглядається такий гамільтоніан:
де описує зв'язок між найближчими сусідніми спінами, , а — глобальне поперечне поле.
Тут ми реалізуємо гамільтоніан із часовою еволюцією для різних масштабів часу та шуму. Ми створюємо 60 схем загалом: 10 безшумових схем різної тротерівської глибини та 50 шумових схем для 10 тротерівських глибин у п'яти масштабах шуму. Для заданого графа зв'язності модель параметризується кількома змінними:
num_steps: Кількість тротерівських кроківJ: Сила зв'язку між з'єднаними вузламиh: Сила зовнішнього магнітного поляdt: Зміна часу за один тротерівський крокinitial_state_angle: Початкове збудження, , що рівномірно застосовується до всіх кубітів
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.

Задай спостережувану та запусти симуляції
Для цієї демонстрації ми моделюємо математичні сподівання середнього двовузлового корелятора:
де відповідає рівномірному обертанню на всіх кубітах, описує тротерівських шарів, а індексують усі з'єднані пари вершин решітки.
Нарешті, ми використовуємо pauli_prop, щоб змоделювати математичні сподівання спостережуваної для кожної зі схем. Для цієї дев'ятикубітної демонстрації ми виконуємо всі симуляції точно. Відкидання паулівських доданків не виконуватиметься; тому відмінності математичних сподівань між різними моделями шуму можна повністю віднести на рахунок похибки гейтів. Процес моделювання складається з чотирьох кроків:
- Еволюціонуй гейти Кліффорда в схемі на її початок за допомогою
pauli_prop.evolve_through_cliffords - Поширюй спостережувану крізь не-кліффордову частину схеми за допомогою
pauli_prop.propagate_through_circuit- Ми виконуємо точні симуляції, дозволяючи спостережуваній зрости до розміру повного паулівського простору,
- Поширюй еволюціоновану спостережувану крізь кліффордову частину схеми за допомогою
SparsePauliOp.evolveз Qiskit - Оціни математичне сподівання відносно нульового стану, , підсумувавши коефіцієнти кожного діагонального доданка в (доданки, що містять або на всіх кубітах)
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.
Спостерігай вплив похибки гейтів на модель
Пам'ятай, що оскільки це дев'ятикубітний експеримент, процедура Pauli propagation є точною, і всю похибку на графіках шуму можна віднести на рахунок похибки гейтів.
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()
