Saltar al contenido principal
Este proyecto está en fase beta. ¿Encontraste un error? Abre un issue en GitHub. ¿Tienes una pregunta o una idea? Pregunta en Discussions — ¡nos encantaría tu opinión!
¿Cómo es la calidad de la traducción en esta página?

Inicio rápido

Esta guía muestra un ejemplo mínimo funcional del paquete qiskit-addon-obp. Usamos la retropropagación de operadores (OBP) para reducir la profundidad de un circuito cuántico absorbiendo en el observable las puertas finales.

Un valor esperado ⟨O⟩=⟨ψ∣U†OU∣ψ⟩\langle O \rangle = \langle \psi | U^\dagger O U | \psi \rangle no cambia si separamos un bloque final de puertas del circuito UU y conjugamos clásicamente el observable con él. OBP aplica esto repetidamente, evaluando parte del circuito de forma clásica para que solo se ejecute un circuito menos profundo en el hardware. El precio es que cada puerta absorbida puede expandir el observable en más términos de Pauli, por lo que la profundidad ahorrada debe sopesarse frente al crecimiento del observable.

Para ver ejemplos de cómo construir flujos de trabajo realistas con esta herramienta y ejecutarlos en hardware cuántico, consulta los tutoriales en IBM Quantum Platform (Tutorial de OBP).

Preparar las entradas para OBP​

OBP recibe como entrada una lista de fragmentos de circuito y un observable. Retropropaga los fragmentos de uno en uno desde el final del circuito hacia el observable, reduciendo la profundidad del circuito a costa de términos de Pauli adicionales en el observable. Aquí generamos un circuito de evolución temporal para un modelo de Heisenberg de 10 qubits y lo dividimos en fragmentos por tipo de puerta.

A continuación dibujamos el circuito original y luego el mismo circuito recombinado con barreras que marcan los límites de los fragmentos; cada fragmento es una unidad que puede absorberse en el observable en un único paso de retropropagación.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils"}
_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.quantum_info import SparsePauliOp
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types

# Generate a circuit to reduce
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 5, 12, 8, 18]
)

hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),
ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),
)

circuit = generate_time_evolution_circuit(
hamiltonian,
time=0.2,
synthesis=LieTrotter(reps=2),
)

# Slice the circuit and define an observable
slices = slice_by_gate_types(circuit)
observable = SparsePauliOp("IIIIIIIIIZ")

print(f"Original circuit depth: {circuit.depth()}")
print(f"Number of slices: {len(slices)}")
print(f"Observable terms: {len(observable)}")
Original circuit depth: 18
Number of slices: 18
Observable terms: 1
# Recombine the slices with barriers to make the slice boundaries visible
sliced_circuit = combine_slices(slices, include_barriers=True)

print("Original circuit:")
display(circuit.draw("mpl", scale=0.6, fold=-1))
print("Sliced circuit (recombined with barriers for visualization)")
sliced_circuit.draw("mpl", scale=0.6, fold=-1)
Original circuit:

Output of the previous code cell

Sliced circuit (recombined with barriers for visualization)

Output of the previous code cell

Reducir la profundidad del circuito con OBP​

Llamamos a backpropagate para absorber fragmentos en el observable. La función devuelve el observable expandido, los fragmentos de circuito por los que no se propagó y metadatos sobre el procedimiento.

Si no se controla, el observable puede crecer hasta 2n2^n términos de Pauli. El operator_budget limita este crecimiento; aquí permitimos como máximo 8 grupos conmutantes por qubits, lo que fija aproximadamente el número de shots necesarios para evaluar el observable en la QPU. La retropropagación se detiene en cuanto absorber el siguiente fragmento superaría el presupuesto, que es exactamente lo que ocurre a continuación: solo se absorben 7 de los 18 fragmentos antes de que el observable llene los 8 grupos conmutantes y el procedimiento se detenga.

Para profundizar más en el circuito, se puede usar el argumento de palabra clave truncation_error_budget de backpropagate para descartar del observable los términos de Pauli de coeficiente pequeño a medida que crece. Esto amortigua el crecimiento del observable a cambio de cierto error proporcional a la magnitud de los términos de Pauli truncados. Los dos presupuestos son complementarios y pueden usarse juntos: operator_budget controla el tamaño del observable, mientras que truncation_error_budget controla el error incurrido al descartar términos.

from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget

max_qwc_groups = 8
bp_obs, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),
)

reduced_circuit = combine_slices(remaining_slices)
num_groups = len(bp_obs.group_commuting(qubit_wise=True))

print(
f"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices."
)
print(
f"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})"
)
print(f"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.")
print(
f"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget."
)
Backpropagated 7 of 18 slices.
Reduced circuit depth: 11 (was 18)
Observable grew from 1 to 18 Pauli terms.
Filled 8 of 8 commuting groups, exhausting the budget.
reduced_circuit.draw("mpl", scale=0.6, fold=-1)

Output of the previous code cell