Przejdź do głównej treści

Cięcie obwodów w celu redukcji głębokości

Szacowany czas użycia: osiem minut na procesorze Eagle (UWAGA: To jest tylko szacunek. Twój czas wykonania może się różnić.)

Tło​

Ten samouczek pokazuje, jak zbudować Qiskit pattern do cięcia bramek w obwodzie kwantowym w celu redukcji głębokości obwodu. Aby uzyskać bardziej szczegółowe informacje na temat cięcia obwodów, odwiedź dokumentację dodatku Qiskit do cięcia obwodów.

Wymagania​

Przed rozpoczęciem tego samouczka upewnij się, że masz zainstalowane następujące elementy:

  • Qiskit SDK v2.0 lub nowszy, z obsługą wizualizacji
  • Qiskit Runtime v0.22 lub nowszy (pip install qiskit-ibm-runtime)
  • Dodatek Qiskit do cięcia obwodów v0.9.0 lub nowszy (pip install qiskit-addon-cutting)

Konfiguracja​

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-ibm-runtime
import numpy as np

from qiskit.circuit.library import EfficientSU2
from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

Krok 1: Mapowanie klasycznych danych wejściowych na problem kwantowy​

Zaimplementujemy nasz wzorzec Qiskit przy użyciu czterech kroków opisanych w dokumentacji. W tym przypadku będziemy symulować wartości oczekiwane na obwodzie o określonej głębokości, tnąc bramki, co powoduje powstanie bramek swap, i wykonując podeksperymenty na płytszych obwodach. Cięcie bramek jest istotne dla Kroku 2 (optymalizacja obwodu do wykonania kwantowego poprzez rozkładanie odległych bramek) i Kroku 4 (przetwarzanie końcowe w celu rekonstrukcji wartości oczekiwanych na oryginalnym obwodzie). W pierwszym kroku wygenerujemy obwód z biblioteki obwodów Qiskit i zdefiniujemy pewne obserwable.

  • Wejście: Klasyczne parametry do zdefiniowania obwodu

  • Wyjście: Abstrakcyjny obwód i obserwable

circuit = EfficientSU2(num_qubits=4, entanglement="circular").decompose()
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
observables = PauliList(["ZZII", "IZZI", "IIZZ", "XIXI", "ZIZZ", "IXIX"])
circuit.draw("mpl", scale=0.8, style="iqp")

Wynik poprzedniej komórki kodu

Krok 2: Optymalizacja problemu do wykonania na sprzęcie kwantowym​

  • Wejście: Abstrakcyjny obwód i obserwable

  • Wyjście: Docelowy obwód i obserwable uzyskane przez cięcie odległych bramek w celu redukcji głębokości po transpilacji

Wybieramy układ początkowy, który wymaga dwóch swapów do wykonania bramek między Qubitami 3 i 0 oraz kolejnych dwóch swapów, aby przywrócić Qubity do ich początkowych pozycji. Wybieramy optimization_level=3, czyli najwyższy poziom optymalizacji dostępny w predefiniowanym menedżerze przejść.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=circuit.num_qubits, simulator=False
)

pm = generate_preset_pass_manager(
optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend
)
transpiled_qc = pm.run(circuit)

Mapa sprzężeń pokazująca Qubity, które będą musiały być zamieniane

print(f"Transpiled circuit depth: {transpiled_qc.depth()}")
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, style="iqp", fold=-1)
Transpiled circuit depth: 103

Wynik poprzedniej komórki kodu

Znajdź i przeciąj odległe bramki: Zastąpimy odległe bramki (bramki łączące nielokalne Qubity 0 i 3) obiektami TwoQubitQPDGate poprzez określenie ich indeksów. cut_gates zastąpi bramki pod podanymi indeksami obiektami TwoQubitQPDGate i zwróci również listę instancji QPDBasis — po jednej dla każdego rozkładu bramki. Obiekt QPDBasis zawiera informacje o tym, jak rozkładać cięte bramki na operacje jednoQubitowe.

# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(circuit.data)
if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}
]

# Decompose distant CNOTs into TwoQubitQPDGate instances
qpd_circuit, bases = cut_gates(circuit, cut_indices)

qpd_circuit.draw("mpl", scale=0.8)

Wynik poprzedniej komórki kodu

Wygeneruj podeksperymenty do uruchomienia na Backend: generate_cutting_experiments przyjmuje obwód zawierający instancje TwoQubitQPDGate i obserwable jako PauliList.

Aby zasymulować wartość oczekiwaną pełnowymiarowego obwodu, z rozkładu kwaziprawnopodobieństwa rozkładanych bramek generowane jest wiele podeksperymentów, które następnie są wykonywane na jednym lub kilku Backend. Liczba próbek pobieranych z rozkładu jest kontrolowana przez num_samples, a jeden łączny współczynnik jest podawany dla każdej unikalnej próbki. Aby uzyskać więcej informacji na temat sposobu obliczania współczynników, zapoznaj się z materiałami objaśniającymi.

# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observables, num_samples=np.inf
)

Dla porównania, widzimy, że podeksperymenty QPD będą płytsze po cięciu odległych bramek: Oto przykład dowolnie wybranego podeksperymentu wygenerowanego z obwodu QPD. Jego głębokość została zredukowana o ponad połowę. Wiele z tych probabilistycznych podeksperymentów musi zostać wygenerowanych i ocenionych, aby zrekonstruować wartość oczekiwaną głębszego obwodu.

# Transpile the decomposed circuit to the same layout
transpiled_qpd_circuit = pm.run(subexperiments[100])

print(f"Original circuit depth after transpile: {transpiled_qc.depth()}")
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}"
)
transpiled_qpd_circuit.draw(
"mpl", scale=0.6, style="iqp", idle_wires=False, fold=-1
)
Original circuit depth after transpile: 103
QPD subexperiment depth after transpile: 46

Wynik poprzedniej komórki kodu

Z drugiej strony, cięcie skutkuje koniecznością dodatkowego próbkowania. Tutaj tniemy trzy bramki CNOT, co powoduje narzut próbkowania wynoszący 939^3. Aby uzyskać więcej informacji na temat narzutu próbkowania wynikającego z cięcia obwodów, zapoznaj się z dokumentacją Circuit Knitting Toolbox.

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0

Krok 3: Wykonanie przy użyciu prymitywów Qiskit​

Wykonaj docelowe obwody ("podeksperymenty") za pomocą prymitywu Sampler.

  • Wejście: Docelowe obwody

  • Wyjście: Rozkłady kwaziprawnopodobieństwa

# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)

# Set up the IBM Quantum Sampler primitive. For a fake backend, this will use a local simulator.
sampler = SamplerV2(backend)

# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()
print(job.job_id())
czypg1r6rr3g008mgp6g

Krok 4: Przetwarzanie końcowe i zwrócenie wyniku w pożądanym formacie klasycznym​

Użyj wyników podeksperymentów, podobiektów obserwowalnych i współczynników próbkowania, aby zrekonstruować wartość oczekiwaną oryginalnego obwodu.

Wejście: Rozkłady kwaziprawnopodobieństwa Wyjście: Zrekonstruowane wartości oczekiwane

reconstructed_expvals = reconstruct_expectation_values(
results,
coefficients,
observables,
)
# Reconstruct final expectation value
final_expval = np.dot(reconstructed_expvals, [1] * len(observables))
print("Final reconstructed expectation value")
print(final_expval)
Final reconstructed expectation value
1.0751342773437473
ideal_expvals = [
Statevector(circuit).expectation_value(SparsePauliOp(observable))
for observable in observables
]
print("Ideal expectation value")
print(np.dot(ideal_expvals, [1] * len(observables)).real)
Ideal expectation value
1.2283177520039992

Ankieta dotycząca samouczka​

Wypełnij tę krótką ankietę, aby przekazać opinię na temat tego samouczka. Twoje spostrzeżenia pomogą nam ulepszyć nasze materiały i doświadczenia użytkownika.

Uwaga: Ta ankieta pochodzi od IBM Quantum i dotyczy treści samouczka (napisanej przez IBM). doQumentation dostarcza stronę internetową, tłumaczenia i wykonywanie kodu — w sprawie opinii na ich temat prosimy zgłosić problem na GitHubie.

Link do ankiety