Krájení Circuit pro snížení hloubky
Odhad využití: osm minut na procesoru Eagle (POZNÁMKA: Jedná se pouze o odhad. Skutečná doba běhu se může lišit.)
Pozadí
Tento tutoriál ukazuje, jak sestavit Qiskit pattern pro krájení Gate v kvantovém Circuit za účelem snížení hloubky Circuit. Podrobnější diskusi o circuit cutting najdeš v dokumentaci doplňku Qiskit pro circuit cutting.
Požadavky
Před zahájením tohoto tutoriálu se ujisti, že máš nainstalováno následující:
- Qiskit SDK v2.0 nebo novější, s podporou vizualizace
- Qiskit Runtime v0.22 nebo novější (
pip install qiskit-ibm-runtime) - Doplněk Qiskit pro circuit cutting v0.9.0 nebo novější (
pip install qiskit-addon-cutting)
Nastavení
# 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_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")
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: Mapování klasických vstupů na kvantový problém
Náš Qiskit pattern implementujeme pomocí čtyř kroků popsaných v dokumentaci. V tomto případě budeme simulovat střední hodnoty na obvodu určité hloubky tím, že rozřízneme Gate, které by jinak vyžadovaly swap gate, a spustíme dílčí experimenty na mělčích Circuit. Krájení Gate je relevantní pro kroky 2 (optimalizace Circuit pro kvantové spuštění rozkladem vzdálených Gate) a 4 (post-processing pro rekonstrukci středních hodnot na původním Circuit). V prvním kroku vygenerujeme Circuit z knihovny Qiskit a definujeme některé observovatelné.
-
Vstup: Klasické parametry pro definici Circuit
-
Výstup: Abstraktní Circuit a observovatelné
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")
Krok 2: Optimalizace problému pro spuštění na kvantovém hardwaru
-
Vstup: Abstraktní Circuit a observovatelné
-
Výstup: Cílový Circuit a observovatelné vzniklé krájením vzdálených Gate za účelem snížení hloubky transpilovánného Circuit
Zvolíme počáteční rozvržení, které vyžaduje dva swapy pro provedení Gate mezi qubit 3 a 0, a další dva swapy pro návrat qubit do jejich počátečních pozic. Zvolíme optimization_level=3, což je nejvyšší úroveň optimalizace dostupná s přednastaveným pass managerem.
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)

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
Nalezení a krájení vzdálených Gate: Vzdálené Gate (Gate spojující nelokální qubit 0 a 3) nahradíme objekty TwoQubitQPDGate zadáním jejich indexů. cut_gates nahradí Gate na zadaných indexech objekty TwoQubitQPDGate a také vrátí seznam instancí QPDBasis — jeden pro každý rozklad Gate. Objekt QPDBasis obsahuje informace o tom, jak rozložit nakrájené Gate na jednoqubitové operace.
# 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)
Generování dílčích experimentů pro spuštění na Backend: generate_cutting_experiments přijímá Circuit obsahující instance TwoQubitQPDGate a observovatelné jako PauliList.
Aby bylo možné simulovat střední hodnotu Circuit plné velikosti, generuje se ze sdruženého kvazipravděpodobnostního rozdělení rozložených Gate mnoho dílčích experimentů, které se pak spouštějí na jednom nebo více Backend. Počet vzorků odebraných z rozdělení je řízen parametrem num_samples a pro každý jedinečný vzorek je uveden jeden kombinovaný koeficient. Více informací o výpočtu koeficientů najdeš ve vysvětlujícím materiálu.
# Generate the subexperiments and sampling coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit, observables=observables, num_samples=np.inf
)
Pro srovnání vidíme, že dílčí experimenty QPD budou po nakrájení vzdálených Gate mělčí: Zde je příklad libovolně zvoleného dílčího experimentu generovaného z QPD Circuit. Jeho hloubka se snížila o více než polovinu. Aby bylo možné rekonstruovat střední hodnotu hlubšího Circuit, musí být vygenerováno a vyhodnoceno mnoho těchto pravděpodobnostních dílčích experimentů.
# 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
Na druhou stranu krájení vede k potřebě extra vzorkování. Zde nakrájíme tři CNOT Gate, čímž vznikne režie vzorkování . Více o režii vzorkování způsobené circuit cutting najdeš v dokumentaci Circuit Knitting Toolbox.
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0
Krok 3: Spuštění pomocí primitiv Qiskit
Spusť cílové Circuit ("dílčí experimenty") pomocí primitivy Sampler.
-
Vstup: Cílové Circuit
-
Výstup: Kvazipravděpodobnostní rozdělení
# 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)
sampler.options.environment.job_tags = ["TUT_CCDR"]
# Submit the subexperiments
job = sampler.run(isa_subexperiments)
# Retrieve the results
results = job.result()
print(job.job_id())
czypg1r6rr3g008mgp6g
Krok 4: Post-processing a vrácení výsledku v požadovaném klasickém formátu
Pomocí výsledků dílčích experimentů, dílčích observovatelných a vzorkovacích koeficientů rekonstruuj střední hodnotu původního Circuit.
Vstup: Kvazipravděpodobnostní rozdělení Výstup: Rekonstruované střední hodnoty
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
Průzkum k tutoriálu
Prosím, vyplň tento krátký průzkum a poskytni zpětnou vazbu k tomuto tutoriálu. Tvoje poznatky nám pomohou zlepšit naši nabídku obsahu a uživatelský zážitek.
Poznámka: Tento průzkum pochází od IBM Quantum a týká se obsahu tutoriálu (napsaného IBM). doQumentation poskytuje web, překlady a spouštění kódu – pro zpětnou vazbu k těmto oblastem prosím otevři GitHub issue.