Przejdź do głównej treści
Ten projekt jest w wersji beta. Znalazłeś błąd? Otwórz zgłoszenie na GitHub. Masz pytanie lub pomysł? Zapytaj w Discussions — chętnie poznamy Twoją opinię!
Jak oceniasz jakość tłumaczenia tej strony?

Szybki start

Ten przewodnik przedstawia minimalny działający przykład pakietu qiskit-addon-obp. Używamy propagacji wstecznej operatorów (OBP), aby zmniejszyć głębokość obwodu kwantowego, wchłaniając końcowe bramki do obserwabli.

Wartość oczekiwana ⟨O⟩=⟨ψ∣U†OU∣ψ⟩\langle O \rangle = \langle \psi | U^\dagger O U | \psi \rangle nie zmienia się, jeśli oderwiemy od obwodu UU końcowy blok bramek i sprzęgniemy nim klasycznie obserwablę. OBP stosuje to wielokrotnie, obliczając część obwodu klasycznie, tak aby na sprzęcie działał tylko płytszy obwód. Ceną jest to, że każda wchłonięta bramka może rozszerzyć obserwablę o kolejne wyrazy Pauliego, dlatego zyskaną głębokość trzeba zestawić ze wzrostem obserwabli.

Aby zobaczyć przykłady budowania realistycznych przepływów pracy za pomocą tego narzędzia i wykonywania ich na sprzęcie kwantowym, zajrzyj do samouczków na IBM Quantum Platform (Samouczek OBP).

Przygotuj dane wejściowe dla OBP​

OBP przyjmuje na wejściu listę wycinków obwodu i obserwablę. Propaguje wstecznie wycinki jeden po drugim od końca obwodu do obserwabli, zmniejszając głębokość obwodu kosztem dodatkowych wyrazów Pauliego w obserwabli. Tutaj generujemy obwód ewolucji w czasie dla 10-kubitowego modelu Heisenberga i dzielimy go na wycinki według typu bramki.

Poniżej rysujemy oryginalny obwód, a następnie ten sam obwód złożony ponownie z barierami wyznaczającymi granice wycinków — każdy wycinek jest jednostką, którą można wchłonąć do obserwabli w jednym kroku propagacji wstecznej.

# 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

Zmniejsz głębokość obwodu za pomocą OBP​

Wywołujemy backpropagate, aby wchłonąć wycinki do obserwabli. Funkcja zwraca rozszerzoną obserwablę, wycinki obwodu, przez które nie przeprowadzono propagacji, oraz metadane dotyczące procedury.

Bez ograniczeń obserwabla może urosnąć do 2n2^n wyrazów Pauliego. Parametr operator_budget ogranicza ten wzrost — tutaj dopuszczamy co najwyżej 8 grup komutujących kubitowo, co z grubsza wyznacza liczbę strzałów potrzebnych do oszacowania obserwabli na QPU. Propagacja wsteczna zatrzymuje się, gdy tylko wchłonięcie następnego wycinka przekroczyłoby budżet, co dokładnie dzieje się poniżej: wchłoniętych zostaje tylko 7 z 18 wycinków, zanim obserwabla wypełni wszystkie 8 grup komutujących i procedura się zatrzyma.

Aby zajść głębiej w obwód, można użyć argumentu nazwanego truncation_error_budget metody backpropagate, który pozwala odrzucać z rosnącej obserwabli wyrazy Pauliego o małych współczynnikach. Dzięki temu wzrost obserwabli jest tłumiony kosztem pewnego błędu proporcjonalnego do wielkości obcinanych wyrazów Pauliego. Oba budżety się uzupełniają i można ich używać razem: operator_budget kontroluje rozmiar obserwabli, a truncation_error_budget kontroluje błąd wprowadzany przez odrzucanie wyrazów.

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