Zum Hauptinhalt springen

Circuit Cutting zur Tiefenreduzierung

Geschätzter Aufwand: Acht Minuten auf einem Eagle-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine Laufzeit kann abweichen.)

Hintergrund

Dieses Tutorial zeigt, wie du ein Qiskit pattern zum Schneiden von Gates in einem Quantenschaltkreis aufbaust, um die Schaltkreistiefe zu reduzieren. Eine ausführlichere Diskussion zum Circuit Cutting findest du in den Docs zum Circuit-Cutting-Qiskit-Addon.

Voraussetzungen

Stelle vor Beginn dieses Tutorials sicher, dass Folgendes installiert ist:

  • Qiskit SDK v2.0 oder höher, mit Unterstützung für Visualisierung
  • Qiskit Runtime v0.22 oder höher (pip install qiskit-ibm-runtime)
  • Circuit-Cutting-Qiskit-Addon v0.9.0 oder höher (pip install qiskit-addon-cutting)

Setup

# 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

Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden

Wir implementieren unser Qiskit-Pattern anhand der vier Schritte, die in den Docs beschrieben sind. In diesem Fall simulieren wir Erwartungswerte eines Schaltkreises einer bestimmten Tiefe, indem wir Gates schneiden, die dabei entstehenden Swap-Gates ersetzen und Teilexperimente auf flacheren Schaltkreisen ausführen. Gate-Cutting ist relevant für Schritt 2 (Schaltkreis für die Quantenausführung optimieren, indem weit entfernte Gates zerlegt werden) und Schritt 4 (Nachverarbeitung zur Rekonstruktion der Erwartungswerte des ursprünglichen Schaltkreises). Im ersten Schritt erzeugen wir einen Schaltkreis aus der Qiskit-Schaltkreisbibliothek und definieren einige Observablen.

  • Eingabe: Klassische Parameter zur Definition eines Schaltkreises
  • Ausgabe: Abstrakter Schaltkreis und Observablen
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")

Ausgabe der vorherigen Code-Zelle

Schritt 2: Problem für die Ausführung auf Quantenhardware optimieren

  • Eingabe: Abstrakter Schaltkreis und Observablen
  • Ausgabe: Zielschaltkreis und Observablen, erzeugt durch das Schneiden weit entfernter Gates zur Reduzierung der transpierten Schaltkreistiefe

Wir wählen ein initiales Layout, das zwei Swaps erfordert, um die Gates zwischen Qubits 3 und 0 auszuführen, sowie zwei weitere Swaps, um die Qubits in ihre Ausgangspositionen zurückzubringen. Wir wählen optimization_level=3, das höchste verfügbare Optimierungsniveau eines voreingestellten Pass-Managers.

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)

Kopplungskarte, die zeigt, welche Qubits getauscht werden müssen

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

Ausgabe der vorherigen Code-Zelle

Weit entfernte Gates finden und schneiden: Wir ersetzen die weit entfernten Gates (Gates, die nicht-lokale Qubits 0 und 3 verbinden) durch TwoQubitQPDGate-Objekte, indem wir deren Indizes angeben. cut_gates ersetzt die Gates an den angegebenen Indizes durch TwoQubitQPDGate-Objekte und gibt außerdem eine Liste von QPDBasis-Instanzen zurück – eine für jede Gate-Zerlegung. Das QPDBasis-Objekt enthält Informationen darüber, wie die geschnittenen Gates in Einzelqubit-Operationen zerlegt werden.

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

Ausgabe der vorherigen Code-Zelle

Die Teilexperimente für die Ausführung auf dem Backend erzeugen: generate_cutting_experiments akzeptiert einen Schaltkreis mit TwoQubitQPDGate-Instanzen sowie Observablen als PauliList.

Um den Erwartungswert des vollständigen Schaltkreises zu simulieren, werden viele Teilexperimente aus der gemeinsamen Quasiwahrscheinlichkeitsverteilung der zerlegten Gates erzeugt und dann auf einem oder mehreren Backends ausgeführt. Die Anzahl der aus der Verteilung gezogenen Stichproben wird durch num_samples gesteuert, wobei ein kombinierter Koeffizient für jede eindeutige Stichprobe angegeben wird. Weitere Informationen zur Berechnung der Koeffizienten findest du im Erklärungsmaterial.

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

Zum Vergleich: Die QPD-Teilexperimente sind nach dem Schneiden weit entfernter Gates flacher: Hier ist ein Beispiel eines beliebig gewählten Teilexperiments, das aus dem QPD-Schaltkreis erzeugt wurde. Seine Tiefe wurde um mehr als die Hälfte reduziert. Viele dieser probabilistischen Teilexperimente müssen erzeugt und ausgewertet werden, um einen Erwartungswert des tieferen Schaltkreises zu rekonstruieren.

# 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

Ausgabe der vorherigen Code-Zelle

Andererseits erfordert das Schneiden zusätzliches Sampling. Hier schneiden wir drei CNOT-Gates, was zu einem Sampling-Overhead von 939^3 führt. Mehr zum Sampling-Overhead beim Circuit Cutting findest du in der Dokumentation des Circuit Knitting Toolbox.

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

Schritt 3: Mit Qiskit-Primitiven ausführen

Führe die Zielschaltkreise ("Teilexperimente") mit dem Sampler-Primitiv aus.

  • Eingabe: Zielschaltkreise
  • Ausgabe: Quasi-Wahrscheinlichkeitsverteilungen
# Transpile the subexperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pm.run(subexperiments)

# 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()
print(job.job_id())
czypg1r6rr3g008mgp6g

Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format

Verwende die Teilexperiment-Ergebnisse, Teil-Observablen und Sampling-Koeffizienten, um den Erwartungswert des ursprünglichen Schaltkreises zu rekonstruieren.

Eingabe: Quasi-Wahrscheinlichkeitsverteilungen Ausgabe: Rekonstruierte Erwartungswerte

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

Tutorial-Umfrage

Bitte nimm an dieser kurzen Umfrage teil, um Feedback zu diesem Tutorial zu geben. Deine Rückmeldungen helfen uns, unsere Inhalte und die Nutzererfahrung zu verbessern.

Link zur Umfrage