Cómo interpretar las visualizaciones de shaded lightcone
El paquete ofrece dos formas de ver un shaded lightcone:
-
draw_shaded_lightconesuperpone las cotas al diagrama del circuito. Es lo mejor para circuitos sobre una cadena 1D de qubits, donde el diagrama del circuito y la disposición física coinciden. -
animate_shaded_lightconeproyecta las cotas sobre el mapa de acoplamiento de la QPU y recorre el circuito capa por capa. Es lo mejor cuando la conectividad de los qubits es más que una línea 1D, donde un diagrama de circuito deja de transmitir qué qubits son vecinos.
Ambas visualizaciones muestran los mismos números subyacentes. Esta guía explica qué significan esos números y detalla los aspectos que son fáciles de malinterpretar.
Configuración
Usa el mismo circuito de Ising con Trotter y el mismo observable que la guía de inicio rápido.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_slc": "qiskit-addon-slc", "samplomatic": "samplomatic"}
_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 Pauli, PauliLindbladMap, QubitSparsePauliList
from qiskit_addon_slc.bounds import (
compute_backward_bounds,
compute_forward_bounds,
merge_bounds,
)
from qiskit_addon_slc.utils import generate_noise_model_paulis
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions
def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):
"""Trotterized transverse-field Ising evolution on a 1D chain."""
circuit = QuantumCircuit(num_qubits)
for _ in range(num_steps):
circuit.rx(rx_angle, range(num_qubits))
circuit.barrier()
for start in (0, 1): # even then odd bonds
for i in range(start, num_qubits - 1, 2):
circuit.rzz(rzz_angle, i, i + 1)
circuit.barrier()
return circuit
num_qubits = 10
circuit = trotter_ising_circuit(
num_qubits, num_steps=4, rx_angle=np.pi / 16, rzz_angle=-np.pi / 3
)
observable = Pauli("I" * num_qubits).compose("Z", [num_qubits // 2])
boxing_pass = generate_boxing_pass_manager(
inject_noise_targets="all",
inject_noise_strategy="individual_modification",
inject_noise_site="after",
twirling_strategy="active",
remove_barriers="never",
)
boxed_circuit = boxing_pass.run(circuit)
noise_model_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_circuit)
)
forward_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable
)
backward_bounds = compute_backward_bounds(boxed_circuit, noise_model_paulis)
rng = np.random.default_rng(42)
noise_rates = {
layer: PauliLindbladMap.from_components(
rng.random(len(terms)) * 5e-3,
QubitSparsePauliList.from_sparse_list(
terms.to_sparse_list(), terms.num_qubits
),
)
for layer, terms in noise_model_paulis.items()
}
merged_bounds = merge_bounds(
boxed_circuit, forward_bounds, backward_bounds, noise_rates
)
print(f"observable: Z on qubit {num_qubits // 2}")
Optimal spacetime partitioning not implemented! Just partitioning list of noisy boxes.
observable: Z on qubit 5
draw_shaded_lightcone
Cada box ruidoso se sombrea según la cota de los errores que ocurren en él: brillante significa una cota grande, y un box que se desvanece en el fondo significa que sus errores tienen poco efecto en el observable. Esos boxes desvanecidos son los candidatos para eliminarse del modelo de ruido mitigado.
from qiskit_addon_slc.visualization import draw_shaded_lightcone
draw_shaded_lightcone(
boxed_circuit, merged_bounds, noise_model_paulis, fold=-1, scale=0.5
)

Notas importantes
1. Cada número es una suma sobre los términos de error que comparten un soporte. Sin filtro, las cotas de
todos los términos de error que actúan sobre el mismo qubit, o sobre el mismo par de qubits, se suman en un solo valor:
los tres tipos de Pauli en un qubit y los nueve en un par. Eso tiene dos consecuencias. Un valor puede
superar 2.0, aunque la cota de ningún término individual pueda (un box etiquetado 4.5 son varios términos que se suman,
no una cota violada). La figura no puede decirte si un valor grande proviene de un solo tipo
de Pauli o de varios comparables; la siguiente sección muestra cómo separarlos.
2. La escala de color empieza en 2.0 y crece con los datos. La escala va de 0 a la
entrada "max_bound" de los metadatos del circuito superpuesto, que es el mayor entre 2.0 y el mayor
valor mostrado. Las sumas sin filtrar suelen superar 2.0, así que la escala se estira para acomodarlas, y el mismo
color puede representar valores distintos en figuras distintas. Cuando todos los valores son pequeños, la
escala se queda en 2.0 y todo se ve uniformemente oscuro (eso es la escala reflejando el tamaño
absoluto, no un fallo de renderizado).
Separar los tipos de Pauli
Usa pauli_filter para ver un tipo cada vez. Un entero selecciona un peso de Pauli; una cadena selecciona
un Pauli específico, comparado con cada término de ruido reducido a su propio soporte no identidad. Así,
"X" selecciona todo error de un solo qubit, sin importar sobre qué qubit actúe, y "XX" selecciona los
de peso 2 (una etiqueta de ancho completo como "XII" no coincide con nada).
from qiskit_addon_slc.visualization import accumulate_filtered_bounds
box_id = next(iter(merged_bounds))
print(f"single-qubit supports of box {box_id!r}:\n")
print(f"{'filter':>8} bounds per qubit")
for pauli_filter in (None, 1, "X", "Y", "Z"):
filtered = accumulate_filtered_bounds(
boxed_circuit, merged_bounds, noise_model_paulis, pauli_filter
)
per_qubit = {
support[0]: round(float(bound), 3)
for support, bound in sorted(filtered[box_id].items())
if len(support) == 1
}
print(f"{pauli_filter!s:>8} {per_qubit}")
single-qubit supports of box 'm0':
filter bounds per qubit
None {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.312, 4: 1.355, 5: 5.111, 6: 1.506, 7: 0.124, 8: 0.019, 9: 0.0}
1 {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.312, 4: 1.355, 5: 5.111, 6: 1.506, 7: 0.124, 8: 0.019, 9: 0.0}
X {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.153, 4: 0.577, 5: 2.0, 6: 0.702, 7: 0.056, 8: 0.01, 9: 0.0}
Y {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.148, 4: 0.607, 5: 2.0, 6: 0.631, 7: 0.057, 8: 0.01, 9: 0.0}
Z {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.011, 4: 0.172, 5: 1.111, 6: 0.172, 7: 0.011, 8: 0.0, 9: 0.0}
La fila sin filtrar es la suma de los tres tipos de Pauli que hay debajo. Filtrar es la única manera de saber si un valor grande sin filtrar proviene de un tipo de error dominante o de varios comparables. Eso importa, porque la mitigación actúa por término, no por sitio.
Pasar el mismo filtro a draw_shaded_lightcone lo registra en el título de la figura, de modo que una figura filtrada
no pueda confundirse con una sin filtrar:
draw_shaded_lightcone(
boxed_circuit,
merged_bounds,
noise_model_paulis,
pauli_filter="X",
fold=-1,
scale=0.5,
)

Esta figura se ve bastante distinta de la no filtrada de arriba, y no solo porque muestre
menos valores. Cada marcador de posición de estas figuras representa un soporte: un solo qubit o un par de
qubits. Sin filtro, un box muestra un marcador de posición en cada qubit y en cada par de qubits
vecinos, así que se extiende por varias columnas. Con "X", solo quedan los soportes de un solo qubit, y
cada box se reduce a una sola columna.
Sin embargo, ninguna de las dos imágenes muestra lo que PEC realmente aplica. Los marcadores de posición son una forma de mostrar cotas, no compuertas anti-ruido. Durante el experimento, cada caja cuyos términos de error se mitigan se trata como una única capa de compuertas de dos qubits, y los Paulis anti-ruido muestreados para sus términos de error se componen en un único Pauli que se aplica a esa capa en su conjunto.
animate_shaded_lightcone
En un dispositivo cuya conectividad no es una línea 1D, un diagrama de circuito ya no muestra qué qubits son
vecinos, por lo que resulta imposible leer la extensión espacial del cono de luz.
animate_shaded_lightcone dibuja en su lugar el mapa de acoplamiento de la QPU y anima las cotas sobre él,
una caja ruidosa por fotograma.
Necesita un CouplingMap, que dispone en una cuadrícula incrustando el grafo del dispositivo, por lo que admite tanto
la conectividad heavy-hex como la cuadrada.
Nota: En esta documentación renderizada la animación es estática. El control deslizante y el botón de reproducción solo funcionan cuando el notebook se ejecuta de forma interactiva, por ejemplo en Jupyter.
from qiskit import transpile
from qiskit.transpiler import CouplingMap
from qiskit_addon_slc.visualization import animate_shaded_lightcone
# A small square-lattice device stands in for a real backend here.
coupling_map = CouplingMap.from_grid(3, 4)
# Lay the chain onto a connected path of physical qubits, snaking through the grid, so the
# transpiler inserts no SWAPs: a SWAP-laden circuit is far more expensive to bound and harder to
# read.
initial_layout = [0, 1, 2, 3, 7, 6, 5, 4, 8, 9]
device_circuit = transpile(
circuit,
coupling_map=coupling_map,
initial_layout=initial_layout,
optimization_level=0,
)
boxed_device_circuit = boxing_pass.run(device_circuit)
device_noise_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_device_circuit),
coupling_map=coupling_map,
circuit=boxed_device_circuit,
)
# The same observable, on the physical qubit the middle of the chain was laid onto.
device_observable = Pauli("I" * device_circuit.num_qubits).compose(
"Z", [initial_layout[num_qubits // 2]]
)
device_bounds = compute_forward_bounds(
boxed_device_circuit, device_noise_paulis, device_observable
)
animate_shaded_lightcone(boxed_device_circuit, device_bounds, coupling_map)

Cómo leer la animación
Cada qubit es un gráfico circular, con una porción por tipo de Pauli. El marcador de un qubit se divide en tres
porciones para sus errores , y , cada una sombreada según su propia cota. Es el filtrado de la
sección anterior incorporado en la figura (no hace falta pauli_filter para distinguir los tipos).
Las aristas llevan las cotas de error de dos qubits. La línea entre dos qubits acoplados se sombrea según la cota de los errores de peso 2 que actúan sobre ese par.
El gris claro significa que no hay datos, no una cota pequeña. Una porción o una arista se dibuja en gris claro cuando el
modelo de ruido de la caja no tiene ningún término de error para ella, por ejemplo en qubits sobre los que la caja no actúa. Se
dibuja en gris en lugar de oscuro para que no se confunda con una cota de cero. Las cajas más allá de un límite
max_num_boxes son diferentes: llevan la cota trivial de 2.0, por lo que se dibujan en el
extremo brillante de la escala.
El control deslizante avanza por cajas, no por el tiempo de manera uniforme. Hay un fotograma por caja ruidosa, en
orden del circuito (o en orden inverso con reverse=True, que traza el cono de luz hacia atrás desde
el observable). Verlo en orden inverso suele ser la dirección más intuitiva: el cono crece
hacia fuera desde los qubits medidos. Cada fotograma lleva la etiqueta del identificador de su caja, la misma clave
con la que el diccionario de cotas almacena esa caja, de modo que el control deslizante también sirve para saltar a una
caja concreta y consultar sus números.
Nota: La disposición de los qubits se recupera del grafo del mapa de acoplamiento incrustándolo en una red
cuadrada. Un mapa de acoplamiento que no se puede incrustar (porque está desconectado o tiene una conectividad demasiado densa
para una cuadrícula) lanza un ValueError en lugar de dibujar una imagen engañosa.
Cuál usar
draw_shaded_lightcone | animate_shaded_lightcone | |
|---|---|---|
| geometría | el diagrama del circuito | el mapa de acoplamiento de la QPU |
| ideal para | cadenas 1D | cualquier conectividad |
| tipos de Pauli | sumados, salvo que se filtren | siempre separados, como porciones circulares |
| tiempo | todas las capas a la vez | una capa por fotograma |
| salida | una figura de matplotlib | una figura de plotly |
Para una cadena 1D, la figura estática es más fácil de asimilar de un vistazo y se reproduce en un artículo. Más allá de 1D, la animación es la única de las dos que muestra fielmente la estructura espacial.