Rychlý start
Tento průvodce ukazuje minimální funkční příklad balíčku qiskit-addon-obp. Pomocí zpětné propagace operátoru (OBP) snižujeme hloubku kvantového obvodu tím, že koncová hradla pohlcujeme do observably.
Střední hodnota se nezmění, když z obvodu odloupneme koncový blok hradel a observablu jím klasicky konjugujeme. OBP to aplikuje opakovaně a část obvodu vyhodnotí klasicky, takže na hardwaru běží jen mělčí obvod. Cenou je, že každé pohlcené hradlo může observablu rozšířit o další Pauliho členy, takže ušetřenou hloubku je třeba poměřit s růstem observably.
Příklady, jak vytvářet realistické pracovní postupy s tímto nástrojem a spouštět je na kvantovém hardwaru, najdeš v tutoriálech na IBM Quantum Platform (Tutoriál OBP).
Příprava vstupů pro OBP
OBP bere jako vstup seznam řezů obvodu a observablu. Řezy zpětně propaguje po jednom od konce obvodu do observably, čímž snižuje hloubku obvodu za cenu dalších Pauliho členů v observabli. Zde vygenerujeme obvod časového vývoje pro Heisenbergův model o 10 qubitech a rozřežeme ho podle typu hradla.
Níže vykreslíme původní obvod a poté tentýž obvod znovu složený s bariérami označujícími hranice řezů — každý řez je jednotka, kterou lze pohltit do observably v jednom kroku zpětné propagace.
# 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:

Sliced circuit (recombined with barriers for visualization)

Snížení hloubky obvodu pomocí OBP
Voláme backpropagate, aby se řezy pohltily do observably. Funkce vrací rozšířenou observablu, řezy obvodu, kterými se nepropagovalo, a metadata o postupu.
Bez omezení může observabla narůst až k Pauliho členům. operator_budget tento růst omezuje — zde povolujeme nejvýše 8 skupin qubit-wise komutujících členů, což zhruba určuje počet shotů potřebných k vyhodnocení observably na QPU. Zpětná propagace se zastaví, jakmile by pohlcení dalšího řezu překročilo rozpočet, což se přesně stane níže: pohltí se jen 7 z 18 řezů, než observabla zaplní všech 8 komutujících skupin a postup se zastaví.
Aby bylo možné proniknout hlouběji do obvodu, lze pomocí klíčového argumentu truncation_error_budget metody backpropagate zahazovat z observably během jejího růstu Pauliho členy s malými koeficienty. To tlumí růst observably výměnou za určitou chybu úměrnou velikosti zkracovaných Pauliho členů. Oba rozpočty se doplňují a lze je používat společně: operator_budget řídí velikost observably, zatímco truncation_error_budget řídí chybu vzniklou zahazováním členů.
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)