Gate Cutting untuk Mengurangi Kedalaman Circuit
Dalam tutorial ini, kita akan mengurangi kedalaman Circuit dengan memotong Gate-Gate yang berjauhan, sehingga menghindari swap gate yang biasanya diperkenalkan oleh proses routing.
Berikut adalah langkah-langkah yang akan kita lakukan dalam pola Qiskit ini:
- Langkah 1: Petakan masalah ke circuit dan operator kuantum:
- Petakan Hamiltonian ke dalam circuit kuantum.
- Langkah 2: Optimalkan untuk hardware target [Menggunakan cutting addon]:
- Potong Circuit dan observable.
- Transpile subeksperimen untuk hardware.
- Langkah 3: Eksekusi pada hardware target:
- Jalankan subeksperimen yang diperoleh dari Langkah 2 menggunakan primitif
Sampler.
- Jalankan subeksperimen yang diperoleh dari Langkah 2 menggunakan primitif
- Langkah 4: Pasca-proses hasil [Menggunakan cutting addon]:
- Gabungkan hasil dari Langkah 3 untuk merekonstruksi nilai ekspektasi dari observable yang dimaksud.
Langkah 1: Petakanβ
Buat Circuit untuk dijalankan di 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)

Tentukan observableβ
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])
Langkah 2: Optimalkanβ
Tentukan Backendβ
Kamu bisa menyediakan fake backend atau hardware backend dari Qiskit Runtime.
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = FakeManilaV2()
Transpile Circuit, visualisasikan swap, dan perhatikan kedalamannyaβ
Kita memilih layout yang memerlukan dua swap untuk mengeksekusi Gate antara Qubit 3 dan 0, dan dua swap lagi untuk mengembalikan Qubit ke posisi awalnya.
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)

Ganti Gate yang berjauhan dengan TwoQubitQPDGate berdasarkan indeksnyaβ
cut_gates akan mengganti Gate pada indeks yang ditentukan dengan TwoQubitQPDGate dan juga mengembalikan daftar instance QPDBasis β satu untuk setiap dekomposisi 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)

Buat subeksperimen untuk dijalankan di Backendβ
generate_cutting_experiments menerima circuit yang berisi instance TwoQubitQPDGate dan observable sebagai PauliList.
Untuk mensimulasikan nilai ekspektasi dari circuit berukuran penuh, banyak subeksperimen dibuat dari distribusi kuasiprobabilitas bersama gate yang didekomposisi, lalu dieksekusi pada satu atau lebih Backend. Jumlah sampel yang diambil dari distribusi dikendalikan oleh num_samples, dan satu koefisien gabungan diberikan untuk setiap sampel yang unik. Untuk informasi lebih lanjut tentang cara koefisien dihitung, lihat materi penjelasan.
Catatan: Argumen observables pada generate_cutting_experiments bertipe PauliList. Koefisien dan fase dari setiap suku observable diabaikan selama dekomposisi masalah dan eksekusi subeksperimen. Keduanya dapat diterapkan kembali saat rekonstruksi nilai ekspektasi.
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
)
Hitung overhead sampling untuk pemotongan yang dipilihβ
Di sini kita memotong tiga Gate CNOT, menghasilkan overhead sampling sebesar .
Untuk informasi lebih lanjut tentang overhead sampling yang ditimbulkan oleh circuit cutting, lihat materi penjelasan.
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 729.0
Tunjukkan bahwa subeksperimen QPD akan lebih dangkal setelah Gate yang berjauhan dipotongβ
Berikut adalah contoh salah satu subeksperimen yang dipilih secara acak dari Circuit QPD. Kedalamannya berkurang lebih dari setengahnya. Banyak subeksperimen probabilistik seperti ini harus dibuat dan dievaluasi untuk merekonstruksi nilai ekspektasi dari circuit yang lebih dalam.
# 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

Siapkan subeksperimen untuk Backendβ
# Transpile the subeperiments to the backend's instruction set architecture (ISA)
isa_subexperiments = pass_manager.run(subexperiments)
Langkah 3: Eksekusiβ
Jalankan subeksperimen menggunakan primitif 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()
Langkah 4: Pasca-prosesβ
Rekonstruksi nilai ekspektasiβ
Rekonstruksi nilai ekspektasi untuk setiap suku observable dan gabungkan untuk merekonstruksi nilai ekspektasi dari observable asal.
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)
Bandingkan nilai ekspektasi yang direkonstruksi dengan nilai ekspektasi eksak dari circuit dan observable asalβ
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