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?

Gate Cutting w celu zmniejszenia głębokości Circuit

W tym samouczku zmniejszymy głębokość Circuit przez cięcie odległych Gate'ów, unikając bramek swap, które w przeciwnym razie zostałyby wprowadzone podczas routingu.

Poniżej przedstawiamy kroki, które wykonamy w ramach tego wzorca Qiskit:

  • Krok 1: Odwzorowanie problemu na obwód i operatory:

    • Odwzorowanie hamiltonianu na obwód kwantowy.
  • Krok 2: Optymalizacja pod kątem docelowego sprzętu [Używa cutting addon]:

    • Tnij Circuit i obserwowalną.
    • Transpilacja subeksperymentów pod kątem sprzętu.

  • Krok 3: Wykonanie na docelowym sprzęcie:

    • Uruchom subeksperymenty uzyskane w Kroku 2 za pomocą prymitywu Sampler.
  • Krok 4: Przetwarzanie końcowe wyników [Używa cutting addon]:

    • Połącz wyniki Kroku 3, aby zrekonstruować wartość oczekiwaną rozpatrywanej obserwowalnej.

Krok 1: Odwzorowanie​

Utwórz Circuit do uruchomienia na backendzie​

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

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_cutting": "qiskit-addon-cutting", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_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")
from qiskit.circuit.library import efficient_su2

circuit = efficient_su2(num_qubits=4, entanglement="circular")
circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)
circuit.draw("mpl", scale=0.8)

Quantum circuit diagram

Podaj obserwowalną​

from qiskit.quantum_info import SparsePauliOp

observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])

Krok 2: Optymalizacja​

Podaj backend​

Możesz podać albo fałszywy backend, albo sprzętowy backend z Qiskit Runtime.

from qiskit_ibm_runtime.fake_provider import FakeManilaV2

backend = FakeManilaV2()

Transpiluj Circuit, zwizualizuj zamiany i zanotuj głębokość​

Wybieramy układ, który wymaga dwóch zamian (swapów) do wykonania Gate'ów między Qubitami 3 i 0 oraz kolejnych dwóch zamian w celu powrotu Qubitów do ich pozycji początkowych.

from qiskit.transpiler import generate_preset_pass_manager

pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, initial_layout=[0, 1, 2, 3]
)

transpiled_qc = pass_manager.run(circuit)
print(f"Transpiled circuit depth: {transpiled_qc.depth(lambda x: len(x.qubits) >= 2)}")
Transpiled circuit depth: 30
transpiled_qc.draw("mpl", scale=0.4, idle_wires=False, fold=-1)

Quantum circuit diagram

Zastąp odległe Gate'y bramkami TwoQubitQPDGate przez podanie ich indeksów​

cut_gates zastąpi Gate'y o podanych indeksach bramkami TwoQubitQPDGate i zwróci listę instancji QPDBasis — po jednej dla każdego rozkładu Gate'a.

from qiskit_addon_cutting import cut_gates

# 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)

Quantum circuit diagram

Wygeneruj subeksperymenty do uruchomienia na backendzie​

generate_cutting_experiments przyjmuje Circuit zawierający instancje TwoQubitQPDGate oraz obserwowalne w postaci PauliList.

Aby zasymulować wartość oczekiwaną Circuit o pełnym rozmiarze, z rozkładu wspólnego quasiprawdopodobieństwa rozłożonych Gate'ów generowanych jest wiele subeksperymentów, które następnie wykonywane są na jednym lub kilku backendach. Liczbę próbek pobieranych z rozkładu kontroluje parametr num_samples, a dla każdej unikalnej próbki podawany jest jeden łączny współczynnik. Więcej informacji na temat sposobu obliczania współczynników znajdziesz w materiałach wyjaśniających.

Uwaga: Argument observables funkcji generate_cutting_experiments jest typu PauliList. Współczynniki i fazy wyrazów obserwowalnej są ignorowane podczas rozkładu problemu i wykonywania subeksperymentów. Można je ponownie zastosować podczas rekonstrukcji wartości oczekiwanej.

import numpy as np
from qiskit_addon_cutting import generate_cutting_experiments

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

Oblicz narzut próbkowania dla wybranych cięć​

Tutaj tniemy trzy Gate'y CNOT, co daje narzut próbkowania wynoszący 939^3.

Więcej informacji na temat nadmiaru próbkowania wynikającego z cięcia Circuit znajdziesz w materiałach wyjaśniających.

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

Zademonstruj, że subeksperymenty QPD będą płytsze po przecięciu odległych Gate'ów​

Poniżej znajduje się przykład dowolnie wybranego subeksperymentu wygenerowanego z Circuit QPD. Jego głębokość została zmniejszona o ponad połowę. Aby zrekonstruować wartość oczekiwaną głębszego Circuit, należy wygenerować i wykonać wiele takich probabilistycznych subeksperymentów.

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

print(
f"Original circuit depth after transpile: {transpiled_qc.depth(lambda x: len(x.qubits) >= 2)}"
)
print(
f"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth(lambda x: len(x.qubits) >= 2)}"
)
transpiled_qpd_circuit.draw("mpl", scale=0.8, idle_wires=False, fold=-1)
Original circuit depth after transpile: 30
QPD subexperiment depth after transpile: 7

Quantum circuit diagram

Przygotuj subeksperymenty dla backendu​

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

Krok 3: Wykonanie​

Uruchom subeksperymenty przy użyciu prymitywu Qiskit Runtime Sampler​

from qiskit_ibm_runtime import SamplerV2

# Set up the Qiskit Runtime 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()

Krok 4: Przetwarzanie końcowe​

Zrekonstruuj wartość oczekiwaną​

Zrekonstruuj wartości oczekiwane dla każdego wyrazu obserwowalnej i połącz je, aby zrekonstruować wartość oczekiwaną dla oryginalnej obserwowalnej.

from qiskit_addon_cutting import reconstruct_expectation_values

reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
observable.paulis,
)
# Reconstruct final expectation value
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

Porównaj zrekonstruowaną wartość oczekiwaną z dokładną wartością oczekiwaną z oryginalnego Circuit i obserwowalnej​

from qiskit_aer.primitives import EstimatorV2

estimator = EstimatorV2()
exact_expval = estimator.run([(circuit, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 0.44018555
Exact expectation value: 0.50497603
Error in estimation: -0.06479049
Relative error in estimation: -0.12830408