Lewati ke konten utama

Mulai dengan circuit cutting menggunakan gate cuts

Versi paket

Kode di halaman ini dikembangkan menggunakan persyaratan berikut. Kami merekomendasikan menggunakan versi ini atau yang lebih baru.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17
qiskit-addon-cutting~=0.10.0

Panduan ini mendemonstrasikan dua contoh kerja gate cuts dengan paket qiskit-addon-cutting. Contoh pertama menunjukkan cara mengurangi kedalaman Circuit (jumlah instruksi Circuit) dengan memotong entangling Gate pada Qubit yang tidak berdekatan yang sebaliknya akan menimbulkan overhead SWAP saat ditranspilasi ke hardware. Contoh kedua membahas cara menggunakan gate cutting untuk mengurangi lebar Circuit (jumlah Qubit) dengan membagi Circuit menjadi beberapa Circuit dengan lebih sedikit Qubit.

Kedua contoh akan menggunakan ansatz efficient_su2 dan merekonstruksi observable yang sama.

Gate cutting untuk mengurangi kedalaman Circuit​

Alur kerja berikut mengurangi kedalaman Circuit dengan memotong Gate yang jauh, sehingga menghindari serangkaian Gate SWAP yang panjang yang sebaliknya akan dimasukkan.

Mulai dengan ansatz efficient_su2, dengan entanglement "circular" untuk memperkenalkan Gate yang jauh.

# Added by doQumentation β€” required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
from qiskit.circuit.library import efficient_su2
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
from qiskit_ibm_runtime import SamplerV2, Batch
from qiskit_aer.primitives import EstimatorV2
from qiskit_addon_cutting import (
cut_gates,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)

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

observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])
print(f"Observable: {observable}")
circuit.draw("mpl", scale=0.8)
Observable: SparsePauliOp(['ZZII', 'IZZI', 'IIZZ', 'XIXI', 'ZIZZ', 'IXIX'],
coeffs=[ 1.+0.j, 1.+0.j, -1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])

Output of the previous code cell

Setiap Gate CNOT antara Qubit q0q_0 dan q3q_3 memperkenalkan dua Gate SWAP setelah transpilasi (dengan asumsi Qubit terhubung dalam garis lurus). Untuk menghindari peningkatan kedalaman ini, kamu bisa mengganti Gate yang jauh ini dengan objek TwoQubitQPDGate menggunakan metode cut_gates(). Fungsi ini juga mengembalikan daftar instans QPDBasis - satu untuk setiap dekomposisi.

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

Output of the previous code cell

Setelah instruksi Gate yang dipotong ditambahkan, subeksperimen akan memiliki kedalaman yang lebih kecil setelah transpilasi dibandingkan Circuit asli. Cuplikan kode di bawah menghasilkan subeksperimen menggunakan generate_cutting_experiments, yang menerima Circuit dan observable untuk direkonstruksi.

Catatan tentang jumlah sampel

Argumen num_samples menentukan berapa banyak sampel yang diambil dari distribusi quasi-probabilitas dan menentukan akurasi koefisien yang digunakan untuk rekonstruksi. Melewatkan tak terhingga (np.inf) akan memastikan semua koefisien dihitung secara tepat. Baca dokumen API tentang generating weights dan generating cutting experiments untuk informasi lebih lanjut.

Setelah subeksperimen dihasilkan, kamu bisa mentranspilasi dan menggunakan primitif Sampler untuk mengambil sampel distribusi dan merekonstruksi nilai ekspektasi yang diestimasi. Blok kode berikut menghasilkan, mentranspilasi, dan mengeksekusi subeksperimen. Kemudian merekonstruksi hasilnya dan membandingkannya dengan nilai ekspektasi yang tepat.

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

# Set a backend to use and transpile the subexperiments
backend = FakeManilaV2()
pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend
)
isa_subexperiments = pass_manager.run(subexperiments)

# Set up the Qiskit Runtime Sampler primitive, submit the subexperiments, and retrieve the results
sampler = SamplerV2(backend)
job = sampler.run(isa_subexperiments, shots=4096 * 3)
results = job.result()

# Reconstruct the results
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
observable.paulis,
)

# Apply the coefficients of the original observable
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

estimator = EstimatorV2()
exact_expval = (
estimator.run([(circuit, observable, [0.4] * len(circuit.parameters))])
.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.49812826
Exact expectation value: 0.50497603
Error in estimation: -0.00684778
Relative error in estimation: -0.0135606
Catatan tentang koefisien observable

Untuk merekonstruksi nilai ekspektasi secara akurat, koefisien dari observable asli (yang berbeda dari koefisien dalam output generate_cutting_experiments()) harus diterapkan pada output rekonstruksi, karena informasi ini hilang saat eksperimen pemotongan dihasilkan atau saat observable diperluas.

Biasanya koefisien ini bisa diterapkan melalui numpy.dot() seperti yang ditunjukkan di atas.

Gate cutting untuk mengurangi lebar Circuit​

Bagian ini mendemonstrasikan penggunaan gate cutting untuk mengurangi lebar Circuit. Mulai dengan efficient_su2 yang sama tapi gunakan entanglement "linear".

qc = efficient_su2(4, entanglement="linear", reps=2)
qc.assign_parameters([0.4] * len(qc.parameters), inplace=True)

observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])
print(f"Observable: {observable}")

qc.draw("mpl", scale=0.8)
Observable: SparsePauliOp(['ZZII', 'IZZI', 'IIZZ', 'XIXI', 'ZIZZ', 'IXIX'],
coeffs=[ 1.+0.j, 1.+0.j, -1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])

Output of the previous code cell

Kemudian hasilkan subcircuit dan subobservable yang akan dieksekusi menggunakan fungsi partition_problem(). Fungsi ini menerima Circuit, observable, dan skema partisi opsional, lalu mengembalikan Circuit dan observable yang dipotong dalam bentuk dictionary.

Partisi didefinisikan oleh string label berbentuk "AABB" di mana setiap label dalam string ini sesuai dengan Qubit pada indeks yang sama dari argumen circuit. Qubit yang berbagi label partisi yang sama dikelompokkan bersama, dan Gate non-lokal yang mencakup lebih dari satu partisi akan dipotong.

Catatan

Kwarg observables ke partition_problem bertipe PauliList. Koefisien dan fase suku observable diabaikan selama dekomposisi masalah dan eksekusi subeksperimen. Koefisien dan fase bisa diterapkan kembali selama rekonstruksi nilai ekspektasi.

partitioned_problem = partition_problem(
circuit=qc, partition_labels="AABB", observables=observable.paulis
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
print(f"Subobservables: {subobservables}")
subcircuits["A"].draw("mpl", scale=0.8)
Sampling overhead: 81.0
Subobservables: {'A': PauliList(['II', 'ZI', 'ZZ', 'XI', 'ZZ', 'IX']), 'B': PauliList(['ZZ', 'IZ', 'II', 'XI', 'ZI', 'IX'])}

Output of the previous code cell

subcircuits["B"].draw("mpl", scale=0.8)

Output of the previous code cell

Langkah berikutnya adalah menggunakan subcircuit dan subobservable untuk menghasilkan subeksperimen yang akan dieksekusi pada QPU menggunakan metode generate_cutting_experiments.

Untuk mengestimasi nilai ekspektasi Circuit berukuran penuh, banyak subeksperimen dihasilkan dari distribusi quasi-probabilitas joint Gate yang didekomposisi kemudian dieksekusi pada satu atau lebih QPU. Jumlah sampel yang diambil dari distribusi ini dikendalikan oleh argumen num_samples.

Blok kode berikut menghasilkan subeksperimen dan mengeksekusinya menggunakan primitif Sampler pada simulator lokal. (Untuk menjalankan ini pada QPU, ubah backend ke sumber daya QPU yang kamu pilih.)

subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)

# Set a backend to use and transpile the subexperiments
backend = FakeManilaV2()
pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend
)
isa_subexperiments = {
label: pass_manager.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

# Submit each partition's subexperiments to the Qiskit Runtime Sampler
# primitive, in a single batch so that the jobs will run back-to-back.
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
jobs = {
label: sampler.run(subsystem_subexpts, shots=4096 * 3)
for label, subsystem_subexpts in isa_subexperiments.items()
}

# Retrieve results
results = {label: job.result() for label, job in jobs.items()}

Terakhir, nilai ekspektasi Circuit penuh direkonstruksi menggunakan metode reconstruct_expectation_values.

Blok kode di bawah merekonstruksi hasilnya dan membandingkannya dengan nilai ekspektasi yang tepat.

# Get expectation values for each observable term
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)

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

estimator = EstimatorV2()
exact_expval = (
estimator.run([(qc, observable, [0.4] * len(qc.parameters))])
.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.53571896
Exact expectation value: 0.56254612
Error in estimation: -0.02682716
Relative error in estimation: -0.04768882

Langkah selanjutnya​

Source: IBM Quantum docs β€” updated 13 Feb 2026
English version on doQumentation β€” updated 7 Mei 2026
This translation based on the English version of 11 Mar 2026