Přeskočit na hlavní obsah

Gate Cutting pro snížení hloubky Circuit

V tomto tutoriálu snížíme hloubku Circuit tím, že přestřihneme vzdálené Gate, čímž se vyhneme SWAP Gate, které by jinak byly zavedeny při routování.

Toto jsou kroky, které v tomto Qiskit vzoru provedeme:

  • Krok 1: Namapování problému na kvantové Circuit a operátory:
    • Namapuj hamiltonián na kvantový Circuit.
  • Krok 2: Optimalizace pro cílový hardware [Využívá cutting addon]:
    • Přestřihni Circuit a observablu.
    • Transpiluj dílčí experimenty pro hardware.
  • Krok 3: Spuštění na cílovém hardwaru:
    • Spusť dílčí experimenty získané v Kroku 2 pomocí primitiva Sampler.
  • Krok 4: Následné zpracování výsledků [Využívá cutting addon]:
    • Zkombinuj výsledky Kroku 3 a rekonstruuj očekávanou hodnotu sledované observably.

Krok 1: Mapování

Vytvoření Circuit pro spuštění na Backend

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
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)

Diagram kvantového Circuit

Specifikace observably

from qiskit.quantum_info import SparsePauliOp

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

Krok 2: Optimalizace

Specifikace Backend

Můžeš zadat buď falešný Backend, nebo hardwarový Backend z Qiskit Runtime.

from qiskit_ibm_runtime.fake_provider import FakeManilaV2

backend = FakeManilaV2()

Transpilace Circuit, vizualizace SWAP a zaznamenání hloubky

Zvolíme rozvržení, které vyžaduje dva SWAP pro provedení Gate mezi Qubit 3 a 0 a další dva SWAP pro vrácení Qubit do jejich počátečních pozic.

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)

Diagram kvantového Circuit

Nahrazení vzdálených Gate pomocí TwoQubitQPDGate zadáním jejich indexů

cut_gates nahradí Gate na zadaných indexech instancemi TwoQubitQPDGate a také vrátí seznam instancí QPDBasis — jednu pro každý rozklad Gate.

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)

Diagram kvantového Circuit

Generování dílčích experimentů pro spuštění na Backend

generate_cutting_experiments přijímá Circuit obsahující instance TwoQubitQPDGate a observably jako PauliList.

Aby bylo možné simulovat očekávanou hodnotu Circuit v plné velikosti, generuje se z sdružené kvazipravděpodobnostní distribuce rozložených Gate mnoho dílčích experimentů, které se poté spouštějí na jednom nebo více Backend. Počet vzorků odebraných z distribuce je řízen parametrem num_samples a pro každý jedinečný vzorek je uveden jeden kombinovaný koeficient. Více informací o způsobu výpočtu koeficientů najdeš v vysvětlujícím materiálu.

Poznámka: Argument observables funkce generate_cutting_experiments je typu PauliList. Koeficienty a fáze členů observably jsou při rozkladu problému a spouštění dílčích experimentů ignorovány. Mohou být znovu použity při rekonstrukci očekávané hodnoty.

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
)

Výpočet vzorkovací režie pro zvolené přestřižení

Zde přestřihneme tři CNOT Gate, což má za následek vzorkovací režii 939^3.

Více informací o vzorkovací režii způsobené přestřižením Circuit najdeš v vysvětlujícím materiálu.

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

Ukázka, že dílčí experimenty QPD budou po přestřižení vzdálených Gate mělčí

Zde je příklad libovolně zvoleného dílčího experimentu generovaného z QPD Circuit. Jeho hloubka byla snížena o více než polovinu. Aby bylo možné rekonstruovat očekávanou hodnotu hlubšího Circuit, je třeba vygenerovat a vyhodnotit mnoho těchto probabilistických dílčích experimentů.

# 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

Diagram kvantového Circuit

Příprava dílčích experimentů pro Backend

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

Krok 3: Spuštění

Spuštění dílčích experimentů pomocí primitiva 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: Následné zpracování

Rekonstrukce očekávané hodnoty

Rekonstruuj očekávané hodnoty pro každý člen observably a zkombinuj je pro rekonstrukci očekávané hodnoty původní observably.

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)

Porovnání rekonstruované očekávané hodnoty s přesnou očekávanou hodnotou původního Circuit a observably

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