Inicio rápido
Esta guía muestra un ejemplo mínimo funcional del paquete qiskit-paulice. Usamos comprobaciones de Pauli espaciotemporales para detectar errores durante la ejecución de un circuito Clifford y luego posseleccionamos únicamente las muestras en las que no se detectó ningún error. Para un flujo de trabajo de extremo a extremo más explicativo que cuantifica la mejora de fidelidad, consulta el tutorial detallado.
Pasos del flujo de trabajo
- Prepara las entradas: un circuito Clifford de carga útil, los qubits objetivo/auxiliares que alojarán las comprobaciones y un modelo de ruido.
- Encuentra buenas comprobaciones de Pauli espaciotemporales y añádelas al circuito.
- Muestrea el circuito con comprobaciones.
- Posselecciona únicamente las muestras en las que no se detectó ningún error.
1. Prepara las entradas
add_pauli_checks toma un circuito Clifford que termina en mediciones (o, con el argumento stabilizers, uno sin mediciones, como en la guía de códigos espaciotemporales), los qubits objetivo que se usarán para implementar las comprobaciones y un modelo de ruido que sirve para puntuar las comprobaciones candidatas. Aquí construimos un circuito Clifford aleatorio poco profundo, lo distribuimos sobre una cadena 1D de qubits físicos en ibm_boston, emparejamos cada qubit de la carga útil con un qubit auxiliar vecino (de modo que una comprobación no necesite puertas SWAP) e inferimos un modelo de ruido despolarizante aproximado a partir de los datos de benchmarking del backend.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "qiskit_paulice": "qiskit-paulice"}
_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_ibm_runtime import QiskitRuntimeService
from qiskit_paulice.layout import get_check_qubits
from qiskit_paulice.noise_models import NoiseModel
# Backend and a 1D chain of physical qubits to run on
backend = QiskitRuntimeService().backend("ibm_boston")
layout = [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133]
# A shallow brickwork random Clifford payload circuit
rng = np.random.default_rng(1764)
circuit = QuantumCircuit(len(layout))
circuit.h(range(circuit.num_qubits))
for d in range(4):
for i in range(d % 2, circuit.num_qubits - 1, 2):
circuit.cz(i, i + 1)
for q in range(circuit.num_qubits):
if rng.integers(0, 2):
circuit.sx(q)
if rng.integers(0, 2):
circuit.s(q)
circuit.measure_all()
# Pair each payload qubit with a neighboring ancilla to host a check (no SWAPs needed)
target_qubits, ancilla_qubits = get_check_qubits(backend.coupling_map, layout)
# A rough depolarizing noise model from backend benchmark data, used to score checks
noise_model = NoiseModel.from_backend(
backend, layout, uniform_gate_noise=True
)
circuit.draw("mpl", fold=-1)

2. Encuentra y añade comprobaciones de Pauli espaciotemporales
add_pauli_checks busca comprobaciones eficaces y de bajo peso en cada qubit objetivo y las añade al circuito, devolviendo una secuencia de instancias de CheckedCircuit. Las instancias de CheckedCircuit contienen un número creciente de comprobaciones, desde 0 comprobaciones hasta una comprobación por qubit objetivo. Cada comprobación mejora la capacidad de detección de errores a costa de un ligero aumento de la profundidad del circuito. En este ejemplo usaremos el circuito con una comprobación en cada qubit objetivo.
from qiskit_paulice import add_pauli_checks
# The circuit has virtual qubits, so we specify our target qubits with virtual indices
target_qubits_v = [layout.index(q) for q in target_qubits]
# Add spacetime Pauli checks
checked_circuit = add_pauli_checks(circuit, target_qubits_v, noise_model)[-1]
checked_circuit.circuit.draw("mpl", fold=-1, scale=0.4, idle_wires=False)

3. Muestrea el circuito con comprobaciones
Antes de muestrear, transpilamos el circuito con comprobaciones sobre qubits físicos (initial_layout = layout + ancilla_qubits) y al conjunto de puertas base nativo de ibm_boston. Para emular una QPU ruidosa, muestreamos con un backend estabilizador ruidoso de Aer cuyas tasas de error coinciden con el noise_model usado para elegir las comprobaciones.
from qiskit import transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel as AerNoiseModel
from qiskit_aer.noise import ReadoutError, depolarizing_error
# Transpile once: lay the checked circuit out on our qubits and into the native basis
isa_circuit = transpile(
checked_circuit.circuit,
backend,
initial_layout=layout + ancilla_qubits,
optimization_level=0,
)
# Build an Aer noise model matching the depolarizing model used to pick checks
aer_noise = AerNoiseModel()
aer_noise.add_all_qubit_quantum_error(
depolarizing_error(noise_model.gate_noise, 2), ["cz"]
)
p = noise_model.readout_noise
aer_noise.add_all_qubit_readout_error(ReadoutError([[1 - p, p], [p, 1 - p]]))
simulator = AerSimulator(method="stabilizer", noise_model=aer_noise)
counts = (
simulator.run(isa_circuit, shots=1000, seed_simulator=1764)
.result()
.get_counts()
)
print(f"sampled {sum(counts.values())} shots")
sampled 1000 shots
4. Posselecciona las muestras sin errores detectados
get_postselection_method asigna a cada shot su vector de síndrome; conservamos solo los shots con síndrome totalmente cero — aquellos en los que ninguna comprobación detectó un error. Descartar los shots marcados elimina los errores detectados de la distribución restante, lo que mejora su fidelidad a costa de una menor tasa de muestreo. Consulta el tutorial para una comparación cuantitativa de la fidelidad.
# Keep only the shots in which no check reported an error
ps_fn = checked_circuit.get_postselection_method()
counts_postselected = {
bs: n for bs, n in counts.items() if not ps_fn(bs).any()
}
kept, total = sum(counts_postselected.values()), sum(counts.values())
print(f"kept {kept} of {total} shots ({kept / total:.0%})")
kept 927 of 1000 shots (93%)