Démarrage rapide
Ce guide présente un exemple minimal fonctionnel du package qiskit-addon-pna. Nous utilisons l'absorption de bruit propagé (PNA) pour construire une observable qui mitige le bruit. À partir d'un circuit et d'un modèle de bruit de Pauli-Lindblad, la PNA propage classiquement l'observable à travers le canal de bruit inverse. Mesurer l'observable obtenue sur le QPU bruité mitige le bruit de porte appris.
Pour voir comment construire un workflow réaliste et l'exécuter sur du matériel quantique avec le modèle d'exécution dirigée, y compris l'apprentissage du modèle de bruit avec NoiseLearnerV3, consulte le tutoriel PNA sur l'IBM Quantum Platform.
1. Préparer les entrées pour la PNA
La PNA prend en entrée un circuit, un modèle de bruit et une observable. Ici, nous construisons un modèle d'Ising à champ transverse trotterisé de 10 qubits sur une chaîne 1D. Nous générons un modèle de bruit de Pauli-Lindblad 2-local aléatoire pour chaque porte d'intrication et l'intégrons sous forme d'instruction Qiskit Aer PauliLindbladError juste avant cette porte. Nous choisissons une observable de Pauli-Z de poids 4 à mesurer.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_pna": "qiskit-addon-pna", "qiskit_aer": "qiskit-aer"}
_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")
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp, pauli_basis
from qiskit_aer.noise import PauliLindbladError
def random_pauli_lindblad_noise(generators, seed, noise_scale=2e-3):
rates = np.random.default_rng(seed).random(len(generators)) * noise_scale
return PauliLindbladError(generators, rates)
def ising_circuit(
num_qubits,
layers,
edge_noise=None,
*,
num_steps=3,
rx_angle=np.pi / 8,
rzz_angle=-np.pi / 2,
):
"""Trotterized transverse-field Ising model; edge_noise=None gives the noiseless circuit."""
qc = QuantumCircuit(num_qubits)
for _ in range(num_steps):
qc.rx(rx_angle, range(num_qubits))
for layer in layers:
for edge in layer:
if edge_noise is not None:
qc.append(
edge_noise[edge], edge
) # inject synthetic gate noise
qc.rzz(rzz_angle, *edge)
return qc
num_qubits = 10
# Two entangling layers per Trotter step: even and odd bonds of a 1D chain
layers = [
[(i, i + 1) for i in range(0, num_qubits - 1, 2)],
[(i, i + 1) for i in range(1, num_qubits - 1, 2)],
]
edges = [edge for layer in layers for edge in layer]
# Random 2-local Pauli-Lindblad noise, one instance per entangling gate
two_qubit_paulis = SparsePauliOp(
[p for p in pauli_basis(2) if np.sum(p.x + p.z)]
).paulis
edge_noise = {
edge: random_pauli_lindblad_noise(two_qubit_paulis, seed=1234 + j)
for j, edge in enumerate(edges)
}
noisy_circuit = ising_circuit(num_qubits, layers, edge_noise)
# A single weight-4 observable: <Z3 Z4 Z5 Z6>
observable = SparsePauliOp.from_sparse_list(
[("ZZZZ", [3, 4, 5, 6], 1.0)], num_qubits=num_qubits
)
noisy_circuit.draw("mpl", fold=-1, scale=0.6)

2. Générer l'observable qui mitige le bruit
generate_noise_mitigating_observable propage chaque générateur de Pauli du canal de bruit inverse vers l'avant jusqu'à la fin du circuit. L'observable est ensuite rétropropagée à travers le canal de bruit inverse, ce qui renvoie une nouvelle observable . Trois paramètres clés influent sur le coût de calcul :
-
max_err_terms: le nombre de termes conservés dans chaque générateur d'anti-bruit au fil de sa propagation vers l'avant. -
max_obs_terms: le nombre de termes conservés dans . -
atol: les termes dont la magnitude du coefficient est inférieure à ce seuil sont supprimés.
Pour ce petit circuit proche de Clifford, nous fixons des limites de termes élevées et utilisons un atol modeste, de sorte que reste petite et que nous puissions mesurer tous ses termes.
Remarque : cette fonction utilise multiprocessing de Python. Lors de l'exécution en tant que script, appelle-la à l'intérieur d'une garde if __name__ == "__main__":.
from qiskit_addon_pna import generate_noise_mitigating_observable
mitigating_observable = generate_noise_mitigating_observable(
noisy_circuit,
observable,
max_err_terms=100_000,
max_obs_terms=100_000,
atol=1e-5,
num_processes=4,
)
print(f"Original observable: {len(observable)} term")
print(f"Noise-mitigating observable: {len(mitigating_observable)} terms")
Original observable: 1 term
Noise-mitigating observable: 207 terms
3. Mitiger les erreurs de porte en mesurant l'observable qui mitige le bruit
Ici, nous voyons que la nouvelle observable mitige efficacement le bruit de porte affectant l'expérience.
import matplotlib.pyplot as plt
from qiskit_aer.primitives import EstimatorV2
noiseless_circuit = ising_circuit(num_qubits, layers)
# density_matrix method at zero precision -> exact expectation values (no shot noise)
estimator = EstimatorV2(
options={
"backend_options": {"method": "density_matrix"},
"default_precision": 0.0,
}
)
ideal, noisy, mitigated = (
result.data.evs
for result in estimator.run(
[
(noiseless_circuit, observable),
(noisy_circuit, observable),
(noisy_circuit, mitigating_observable),
]
).result()
)
print(f"Ideal (noiseless): {ideal:.4f}")
print(f"Noisy (unmitigated): {noisy:.4f}")
print(f"Mitigated (PNA): {mitigated:.4f}")
fig, ax = plt.subplots()
ax.bar(
["Noisy", "Mitigated"],
[noisy, mitigated],
width=0.6,
color=["#b0b0b0", "#4c4c4c"],
)
ax.axhline(ideal, color="green", linestyle="--", label="Ideal (noiseless)")
ax.set_ylabel(r"$\langle Z_3 Z_4 Z_5 Z_6 \rangle$")
ax.legend()
plt.show()
Ideal (noiseless): 0.8073
Noisy (unmitigated): 0.6431
Mitigated (PNA): 0.8071