Sari la conținutul principal
Acest proiect este în versiune beta. Ai găsit o eroare? Deschide un issue pe GitHub. Ai o întrebare sau o idee? Întreabă în Discussions — ne-ar plăcea să aflăm părerea ta!
Cum este calitatea traducerii pe această pagină?

Simulează sisteme cuantice cu zgomot folosind propagarea Pauli

În acest ghid folosim pachetul pauli-prop pentru a simula clasic dinamica în timp a unui model Ising cu câmp transversal (TFIM) cu nouă qubiți și zgomot, pe o rețea pătrată 3x3. Folosim instrucțiuni PauliLindbladError pentru a defini un canal de zgomot, Λ\Lambda, care acționează asupra unui set de straturi de încâlcire, U\mathcal{U}. Apoi propagăm observabila, OO, înapoi prin circuitul cu zgomot și estimăm valorile de așteptare pentru o varietate de modele de zgomot, precum și pentru cazul fără zgomot.

Noisy EV

Pe măsură ce observabila este propagată înapoi prin circuit, fiecare canal de zgomot, Λk\Lambda_k, asociat stratului de încâlcire, Uk\mathcal{U}_k, atenuează termenii Pauli din OO care anticomută cu generatorii săi Pauli-Lindblad. Mai precis, dacă Gk,iG_{k,i} este un generator Pauli al lui Λk\Lambda_k cu rata γk,i\gamma_{k,i}, atunci un termen Pauli, PP, din OO se transformă astfel: cP↦cPe−2γk,iif {P,Gk,i}=0c_P \mapsto c_P e^{-2\gamma_{k,i}} \quad \text{if } \{P, G_{k,i}\}=0, unde cPc_P este coeficientul lui PP. Odată ce OO a fost propagat până la începutul circuitului, valoarea de așteptare față de starea zero, ∣0⟩⊗N|0\rangle^{\otimes N}, poate fi calculată trivial prin însumarea coeficienților fiecărui termen diagonal din OO (termeni care conțin ZZ sau II pe toți qubiții).

Flux de lucru:

  • Specifică rețeaua TFIM și folosește colorarea muchiilor pentru a identifica un set minim de straturi de încâlcire
  • Generează modele de zgomot sintetice, Λk\Lambda_k, pentru fiecare strat de încâlcire unic, UkU_k
    • Creează modele de zgomot de diverse scări pentru a studia impactul zgomotului porților asupra sistemului
  • Creează circuite cuantice fără zgomot și cu zgomot pentru diversele adâncimi și scări de zgomot de interes
    • În circuitele cu zgomot, instrucțiunile PauliLindbladError sunt inserate înaintea fiecărui strat de încâlcire
  • Folosește propagarea Pauli pentru a simula valorile de așteptare exacte ale sistemului la diverse adâncimi
    • Pentru nouă qubiți, aceasta se realizează lăsând OO să crească până la 494^9 termeni, acoperind întregul spațiu Pauli
  • Folosește propagarea Pauli pentru a simula valorile de așteptare cu zgomot
  • Observă cum creșterea zgomotului porților degradează acuratețea modelului cuantic

Generează o rețea pătrată 3x3 și găsește o 4-colorare a muchiilor​

Vârfurile din graf reprezintă qubiți, iar muchiile reprezintă o conexiune între doi qubiți. Colorarea muchiilor corespunde straturilor de încâlcire unice din circuitul cuantic, astfel încât porțile de pe conexiuni asociate unor culori diferite nu pot fi aplicate simultan.

Identificarea unui set minim de straturi de încâlcire unice este adesea importantă pentru implementarea unor protocoale eficiente de învățare a zgomotului, deoarece zgomotul fiecărui strat trebuie învățat independent. Cu cât trebuie să învățăm mai multe straturi, cu atât avem nevoie de mai multe măsurători (shots) de la QPU. Pentru această demonstrație, folosim informațiile despre straturi pentru a construi circuite cu zgomot și a injecta instrucțiuni PauliLindbladError din qiskit-aer înaintea fiecărui strat de încâlcire, pentru a modela zgomotul porților 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.

Output of the previous code cell

Generează modele de zgomot sintetice​

Înainte de a crea circuitele cuantice, generăm un model de zgomot (o instanță PauliLindbladError) pentru fiecare dintre straturile de încâlcire. Le vom încorpora mai târziu ca instrucțiuni în circuitele noastre cuantice. Pentru fiecare strat, generăm canale de zgomot de scări variate. Mai precis, generăm modele de zgomot cu Error Per Layered Gate (EPLG) de aproximativ .0004, .0008, .0012, .0016, și .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
]

Creează circuitele cuantice​

Pentru această demonstrație, simulăm dinamica în timp a unui model Ising cu câmp transversal (TFIM) pentru un număr crescător de pași Trotter (1-10 pași). Pentru fiecare dintre cele 10 adâncimi de circuit, simulăm efectul zgomotului porților, date fiind modele de zgomot de scări variate (EPLGs = .0004, .0008, .0012, .0016, .002). Zgomotul este inserat în QuantumCircuit ca instrucțiune PauliLindbladError din Qiskit Aer. Hamiltonianul considerat este:

H=−J∑⟨i,j⟩ZiZj+h∑iXiH = -J\sum\limits_{\langle i,j \rangle} Z_iZ_j + h\sum\limits_iX_i

unde J>0J>0 descrie cuplajul dintre spinii vecini cei mai apropiați, i<ji<j, iar hh este câmpul transversal global.

Aici implementăm Hamiltonianul evoluat în timp pentru diverse scări de timp și de zgomot. Creăm 60 de circuite în total: 10 circuite fără zgomot cu adâncime Trotter variabilă și 50 de circuite cu zgomot pentru cele 10 adâncimi Trotter, pe cinci scări de zgomot. Dat fiind un graf de conectivitate, modelul este parametrizat de câteva variabile:

  • num_steps: Numărul de pași Trotter
  • J: Intensitatea cuplajului dintre situsurile conectate
  • h: Intensitatea câmpului magnetic extern
  • dt: Variația timpului pe parcursul unui pas Trotter
  • initial_state_angle: O excitație inițială, Ry(θ)R_y(\theta), aplicată uniform tuturor qubiților
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.

Output of the previous code cell

Specifică observabila și rulează simulările​

Pentru această demonstrație, simulăm valorile de așteptare ale corelatorului mediu pe două situsuri:

⟨O⟩=⟨Ztot2(s)⟩=1N2∑⟨Ψ(θ)∣(U†)sZjZk(U)s∣Ψ(θ)⟩\langle O \rangle = \langle Z_{tot}^2(s) \rangle = \frac{1}{N^2}\sum \langle \Psi(\theta)|(\mathscr{U}^{\dagger})^sZ_jZ_k(\mathscr{U})^s|\Psi(\theta) \rangle

unde Ψ(θ)\Psi(\theta) corespunde unei rotații uniforme Ry(θ)R_y(\theta) pe toți qubiții, Us\mathscr{U}^s descrie ss straturi Trotter, iar (j,k)(j,k) indexează toate perechile de vârfuri conectate din rețea.

În final, folosim pauli_prop pentru a simula valorile de așteptare ale observabilei pentru fiecare dintre circuite. Pentru această demonstrație cu nouă qubiți, efectuăm toate simulările exact. Nu se va efectua nicio trunchiere a propagării Pauli; prin urmare, diferențele dintre valorile de așteptare ale diferitelor modele de zgomot pot fi atribuite în întregime erorii porților. Procesul de simulare se desfășoară în patru pași:

  • Evoluează porțile Clifford din circuit până la începutul circuitului folosind pauli_prop.evolve_through_cliffords
  • Propagă observabila prin partea non-Clifford a circuitului folosind pauli_prop.propagate_through_circuit
    • Efectuăm simulări exacte permițând observabilei să crească până la dimensiunea întregului spațiu Pauli, 494^9
  • Propagă observabila evoluată prin partea Clifford a circuitului folosind SparsePauliOp.evolve din Qiskit
  • Estimează valoarea de așteptare față de starea zero, ∣0⟩⊗N|0\rangle^{\otimes N}, însumând coeficienții fiecărui termen diagonal din OO (termeni care conțin ZZ sau II pe toți qubiții)
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.

Observă efectul erorii porților asupra modelului​

Reține că, deoarece acesta este un experiment cu nouă qubiți, rutina de propagare Pauli este exactă, iar întreaga eroare din graficele de zgomot poate fi atribuită erorii porților.

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()

Output of the previous code cell