Pemotongan Circuit untuk syarat sempadan berkala
Anggaran penggunaan: 2 minit pada ibm_boston (NOTA: Ini hanya anggaran. Masa jalan sebenar anda mungkin berbeza.)
Hasil pembelajaran
-
Gunakan sambungan maya untuk mentranspil rantai berkala tanpa melaraskan laluan gate sempadannya.
-
Gantikan gate tersebut dengan eksperimen pemotongan litar dan bina semula nilai jangkaan daripada hasil Sampler.
-
Bandingkan litar yang dipotong dan tidak dipotong, dengan mengambil kira kedalaman litar dan kos pensampelan.
Prasyarat
Anda harus biasa dengan corak Qiskit, transpilasi, dan primitif Sampler.
Latar belakang
Syarat sempadan berkala menyambungkan qubit terakhir dalam rantai kepada yang pertama. Jika qubit ini berjauhan pada pemproses kuantum, pelaksanaan interaksi mereka memerlukan penghalaan, yang boleh menambah gate dua qubit dan meningkatkan kedalaman litar.
Pemotongan gate menggantikan gate dua qubit dengan gabungan berwajaran operasi qubit tunggal dan pengukuran. Melaksanakan eksperimen ini dan menggabungkan hasilnya membina semula nilai jangkaan bagi litar asal. Ini boleh mengurangkan overhed penghalaan, dengan kos pensampelan tambahan. Lihat penjelasan pemotongan litar untuk penguraian kuasi-kebarangkalian yang mendasarinya.
Anda akan mula-mula mengesahkan aliran kerja ini pada simulator tanpa hingar enam qubit. Kemudian anda akan menerapkannya pada rantai sekurang-kurangnya 30 qubit pada perkakasan dan membandingkan hasil yang dipotong dan tidak dipotong. Litar menggunakan parameter dengan nilai jangkaan yang diketahui secara analitik, jadi perbandingan perkakasan tidak memerlukan simulasi vektor keadaan yang besar.
Keperluan
Sebelum memulakan tutorial ini, pasang:
-
Qiskit SDK v2.3 atau lebih baru, dengan sokongan visualisasi
-
Qiskit Runtime v0.46 atau lebih baru (
pip install 'qiskit-ibm-runtime~=0.46.1') -
Addon Qiskit Circuit cutting v0.10.0 (
pip install 'qiskit-addon-cutting~=0.10.0') -
Qiskit Aer v0.17.2 atau lebih baru (
pip install 'qiskit-aer~=0.17.2')
Contoh perkakasan juga memerlukan akaun IBM Quantum® Platform dengan akses kepada QPU yang mempunyai sekurang-kurangnya 120 qubit dan kelayakan yang disimpan. Contoh simulator berjalan tanpa akaun.
Persediaan
Import alat yang digunakan dalam kedua-dua contoh. Aer menyokong pengukuran perantaraan yang diperkenalkan oleh pemotongan gate.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_cutting": "qiskit-addon-cutting", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "rustworkx": "rustworkx"}
_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 matplotlib.pyplot as plt
import numpy as np
import rustworkx as rx
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary
from qiskit.circuit.library import n_local
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.quantum_info import SparsePauliOp, Statevector
from qiskit.result import sampled_expectation_value
from qiskit.transpiler import CouplingMap, PassManager
from qiskit.transpiler.passes import (
BasisTranslator,
GateDirection,
Optimize1qGatesDecomposition,
)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_aer.primitives import SamplerV2 as AerSampler
from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import (
Batch,
QiskitRuntimeService,
SamplerOptions,
SamplerV2,
)
Contoh simulator berskala kecil
Gunakan gelang enam qubit dengan dua lapisan gate berentanglement. Peranti yang disimulasikan mempunyai kesambungan linear, jadi tepi penutup gelang memerlukan penghalaan melainkan ia dipotong.
Langkah 1: Petakan input klasik ke masalah kuantum
Fungsi berikut mencipta lapisan putaran RX yang diselang-seli dengan gate CX pada tepi gelang yang berselang-seli. Dua pengulangan memberikan dua gate sempadan untuk dipotong. Tetapkan dua lapisan putaran pertama kepada sifar dan lapisan akhir kepada . Output unggul ialah , sehingga fasa global.
Ukur jangkaan Z qubit tunggal purata dan jangkaan ZZ bersebelahan purata, termasuk pasangan berkala:
Nilai unggul masing-masing ialah dan . Parameter ini menyediakan penanda aras untuk aliran kerja; ia tidak menunjukkan pengiraan yang sukar secara klasik.
def build_periodic_problem(num_qubits, num_reps=2):
"""Build a periodic benchmark circuit and its Z and ZZ observables."""
edges = [(i, (i + 1) % num_qubits) for i in range(num_qubits)]
circuit = n_local(
num_qubits,
rotation_blocks="rx",
entanglement_blocks="cx",
entanglement=edges[::2] + edges[1::2],
reps=num_reps,
)
parameters = np.zeros(circuit.num_parameters)
parameters[-num_qubits:] = np.pi
circuit = circuit.assign_parameters(parameters)
terms = [("Z", [i], 1) for i in range(num_qubits)]
terms += [("ZZ", [i, (i + 1) % num_qubits], 1) for i in range(num_qubits)]
observables = SparsePauliOp.from_sparse_list(terms, num_qubits)
return circuit, observables
num_qubits = 6
circuit, observables = build_periodic_problem(num_qubits)
ideal_expvals = np.array(
[
Statevector(circuit).expectation_value(p).real
for p in observables.paulis
]
)
print("Ideal averages:", ideal_expvals.reshape(2, num_qubits).mean(axis=1))
circuit.draw("mpl", style="iqp", fold=60)
Ideal averages: [-1. 1.]

Langkah 2: Optimumkan masalah untuk pelaksanaan perkakasan kuantum
Cipta model peranti dengan peta gandingan linear. Ia membekalkan sasaran kompilasi; pelaksanaan dalam Langkah 3 akan tanpa hingar.
simulation_backend = GenericBackendV2(
num_qubits=num_qubits,
basis_gates=["id", "rz", "sx", "x", "cx"],
coupling_map=CouplingMap.from_line(num_qubits),
seed=1234,
)
initial_layout = list(range(num_qubits))
Tambahkan sambungan maya antara qubit fizikal di hujung rantai. Lakukan transpile sekali menggunakan peta gandingan yang ditambah ini dan asas CX. Sambungan maya menghalang transpiler daripada melaluan gate sempadan, tetapi litar yang terhasil belum boleh dijalankan pada peranti sebenar.
Cari dan potong dua gate CX sempadan, gunakan susun atur maya pada observable, dan jana semua sebutan penguraian dengan num_samples=np.inf. Ini menjadikan pekali penguraian tepat; pengukuran masih menggunakan bilangan shot yang terhingga.
Akhir sekali, terjemahkan eksperimen kepada gate asli peranti dan betulkan arahnya jika perlu. Pas ini mengekalkan susun atur qubit fizikal dan mengelakkan pengulangan pemilihan susun atur serta pelaluan bagi setiap eksperimen. Pembantu ini mengembalikan observable terpeta yang diperlukan untuk pembinaan semula.
def prepare_cut_experiments(circuit, observables, backend, initial_layout):
"""Compile with a virtual edge, cut it, and translate to native gates."""
boundary = {initial_layout[0], initial_layout[-1]}
virtual_map = CouplingMap(list(backend.coupling_map.get_edges()))
virtual_map.make_symmetric()
virtual_map.add_edge(initial_layout[-1], initial_layout[0])
virtual_map.add_edge(initial_layout[0], initial_layout[-1])
virtual_pm = generate_preset_pass_manager(
optimization_level=1,
coupling_map=virtual_map,
basis_gates=["rz", "sx", "x", "cx"],
initial_layout=initial_layout,
seed_transpiler=1234,
)
virtual_circuit = virtual_pm.run(circuit)
cut_indices = [
i
for i, instruction in enumerate(virtual_circuit.data)
if instruction.operation.name == "cx"
and {virtual_circuit.find_bit(q).index for q in instruction.qubits}
== boundary
]
mapped_observables = observables.apply_layout(virtual_circuit.layout)
qpd_circuit, bases = cut_gates(virtual_circuit, cut_indices)
experiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit,
observables=mapped_observables.paulis,
num_samples=np.inf,
)
native_pm = PassManager(
[
BasisTranslator(
SessionEquivalenceLibrary,
target_basis=backend.operation_names,
target=backend.target,
),
GateDirection(backend.coupling_map, target=backend.target),
BasisTranslator(
SessionEquivalenceLibrary,
target_basis=backend.operation_names,
target=backend.target,
),
Optimize1qGatesDecomposition(target=backend.target),
]
)
isa_experiments = native_pm.run(experiments)
print(f"Boundary gates cut: {len(cut_indices)}")
print(f"Number of experiments: {len(isa_experiments)}")
print(
f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}"
)
return isa_experiments, coefficients, mapped_observables
isa_experiments, coefficients, mapped_observables = prepare_cut_experiments(
circuit, observables, simulation_backend, initial_layout
)
Boundary gates cut: 2
Number of experiments: 36
Sampling overhead: 81.0
Setiap penguraian CX mempunyai enam sebutan. Dengan dua potongan dan satu asas pengukuran Z yang dikongsi, ini menghasilkan eksperimen. Overhead persampelan ialah kuantiti yang berbeza: hasil tambah kuasa dua bagi pekali penguraian mutlak ialah . Ia menerangkan kos persampelan untuk mengekalkan ketepatan di bawah pembinaan semula kuasi-kebarangkalian. Lihat penerangan overhead persampelan.
Sebagai perbandingan, lakukan transpile pada salinan litar tanpa potongan yang disertakan pengukuran, menggunakan peta gandingan sebenar. measure_all() merekodkan qubit logik asal dalam daftar meas; transpilation mengekalkan pemetaan bit klasik tersebut, jadi kiraannya boleh dinilai dengan observable asal.
uncut_pm = generate_preset_pass_manager(
optimization_level=1,
backend=simulation_backend,
initial_layout=initial_layout,
seed_transpiler=1234,
)
isa_uncut = uncut_pm.run(circuit.measure_all(inplace=False))
print("Uncut circuit depth:", isa_uncut.depth())
print("Cut circuit depths:", sorted({qc.depth() for qc in isa_experiments}))
Uncut circuit depth: 15
Cut circuit depths: [6, 7, 8, 9, 10, 11, 12, 15]
Langkah 3: Laksanakan menggunakan primitif Qiskit
Jalankan kedua-dua versi dengan primitif Aer Sampler V2. Setiap eksperimen potongan menerima 10,000 shot, begitu juga litar tunggal tanpa potongan. Oleh itu, aliran kerja potongan menggunakan 36 kali ganda shot dalam perbandingan ini. Shot yang sama bagi setiap litar tidak bermakna bajet pelaksanaan keseluruhan adalah sama.
shots = 10_000
sampler = AerSampler(seed=1234)
cut_result = sampler.run(isa_experiments, shots=shots).result()
uncut_result = sampler.run([isa_uncut], shots=shots).result()
Langkah 4: Pasca-proses dan kembalikan keputusan dalam format klasik yang dikehendaki
Bina semula setiap nilai jangkaan Pauli menggunakan observable terpeta dan susunan eksperimen yang sama seperti semasa penjanaan. Kemudian purata nilai pertama untuk dan nilai yang tinggal untuk . Bagi litar tanpa potongan, kira nilai jangkaan terus daripada kiraan yang diukur.
cut_expvals = reconstruct_expectation_values(
cut_result, coefficients, mapped_observables.paulis
)
uncut_counts = uncut_result[0].data.meas.get_counts()
uncut_expvals = [
sampled_expectation_value(uncut_counts, p) for p in observables.paulis
]
def compare_results(cut_expvals, uncut_expvals, num_qubits):
"""Display average Z and periodic ZZ values against the ideal values."""
cut_averages = np.asarray(cut_expvals).reshape(2, num_qubits).mean(axis=1)
uncut_averages = (
np.asarray(uncut_expvals).reshape(2, num_qubits).mean(axis=1)
)
print("Cut averages (Z, ZZ):", cut_averages)
print("Uncut averages (Z, ZZ):", uncut_averages)
print("Absolute cut errors:", np.abs(cut_averages - [-1, 1]))
print("Absolute uncut errors:", np.abs(uncut_averages - [-1, 1]))
fig, ax = plt.subplots(figsize=(7, 4))
positions = np.arange(2)
for offset, values, label, color in [
(-0.25, [-1, 1], "Ideal", "#4589ff"),
(0, cut_averages, "Cut", "#a56eff"),
(0.25, uncut_averages, "Uncut", "#009d9a"),
]:
ax.bar(
positions + offset, values, width=0.25, label=label, color=color
)
ax.set_xticks(positions, ["Average Z", "Average periodic ZZ"])
ax.set_ylabel("Expectation value")
ax.axhline(0, color="black", linewidth=0.8)
ax.legend()
fig.tight_layout()
plt.show()
compare_results(cut_expvals, uncut_expvals, num_qubits)
Cut averages (Z, ZZ): [-1.00100833 0.99778333]
Uncut averages (Z, ZZ): [-1. 1.]
Absolute cut errors: [0.00100833 0.00221667]
Absolute uncut errors: [0. 0.]
Keputusan tanpa hingar sepatutnya hampir dengan nilai unggul. Pembinaan semula dengan shot terhingga boleh berubah-ubah, dan anggaran yang dibina semula boleh terletak di luar . Pemotongan tidak memperbaik keputusan yang sudah tanpa hingar; contoh ini mengesahkan penguraian, pemetaan susun atur dan pembinaan semula sebelum pelaksanaan pada perkakasan.
Contoh perkakasan berskala besar
Gunakan semula aliran kerja pada QPU beroperasi yang paling kurang sibuk dengan sekurang-kurangnya 120 qubit. Pilih laluan terpendek yang terpanjang dalam graf gandingan tidak berarahnya: setiap pasangan berturutan disambungkan, manakala titik hujungnya berjauhan. Ini menyesuaikan rantai dengan peranti yang dipilih dan bukannya bergantung pada susun atur Eagle yang tetap. Kod ini menyemak bahawa rantai mengandungi sekurang-kurangnya 30 qubit dan titik hujungnya bukan jiran.
Blok berikut menggabungkan empat langkah. Gate twirling, measurement twirling dan dynamical decoupling dinyahaktifkan untuk kedua-dua versi. Kedua-dua job dijalankan dalam batch yang sama dan menerima tag tutorial TUT_CCPBC. Eksperimen potongan masih menggunakan lebih banyak shot keseluruhan, jadi bandingkan ketepatan dan penggunaan QPU.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=120
)
# ------------------------- Step 1 -------------------------
graph = backend.coupling_map.graph.to_undirected(multigraph=False)
paths = rx.all_pairs_dijkstra_shortest_paths(graph, lambda _: 1)
initial_layout = list(
max(
(
path
for source_paths in paths.values()
for path in source_paths.values()
),
key=len,
)
)
num_qubits = len(initial_layout)
if num_qubits < 30:
raise ValueError(
"Select a backend with a shortest path of at least 30 qubits."
)
if graph.has_edge(initial_layout[0], initial_layout[-1]):
raise ValueError("The chain endpoints must not be directly connected.")
print(f"Backend: {backend.name}; chain length: {num_qubits}")
print("Initial layout:", initial_layout)
circuit, observables = build_periodic_problem(num_qubits)
# ------------------------- Step 2 -------------------------
isa_experiments, coefficients, mapped_observables = prepare_cut_experiments(
circuit, observables, backend, initial_layout
)
uncut_pm = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
initial_layout=initial_layout,
seed_transpiler=1234,
)
isa_uncut = uncut_pm.run(circuit.measure_all(inplace=False))
print("Uncut circuit depth:", isa_uncut.depth())
print("Cut circuit depths:", sorted({qc.depth() for qc in isa_experiments}))
# ------------------------- Step 3 -------------------------
options = SamplerOptions()
options.dynamical_decoupling.enable = False
options.twirling.enable_gates = False
options.twirling.enable_measure = False
options.environment.job_tags = ["TUT_CCPBC"]
shots = 10_000
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch, options=options)
cut_job = sampler.run(isa_experiments, shots=shots)
uncut_job = sampler.run([isa_uncut], shots=shots)
print("Cut job ID:", cut_job.job_id())
print("Uncut job ID:", uncut_job.job_id())
cut_result = cut_job.result()
uncut_result = uncut_job.result()
# ------------------------- Step 4 -------------------------
cut_expvals = reconstruct_expectation_values(
cut_result, coefficients, mapped_observables.paulis
)
uncut_counts = uncut_result[0].data.meas.get_counts()
uncut_expvals = [
sampled_expectation_value(uncut_counts, p) for p in observables.paulis
]
compare_results(cut_expvals, uncut_expvals, num_qubits)
qpu_seconds = sum(
job.metrics()["usage"]["quantum_seconds"] for job in [cut_job, uncut_job]
)
print(
f"Total QPU usage: {qpu_seconds:.1f} seconds ({qpu_seconds / 60:.2f} minutes)"
)
Backend: ibm_boston; chain length: 33
Initial layout: [0, 1, 2, 3, 16, 23, 24, 25, 37, 45, 44, 43, 56, 63, 64, 65, 77, 85, 86, 87, 97, 107, 106, 105, 117, 125, 124, 123, 136, 143, 142, 141, 140]
Boundary gates cut: 2
Number of experiments: 36
Sampling overhead: 81.0
Uncut circuit depth: 375
Cut circuit depths: [20]
Cut job ID: dagp7fj9k43c73admp40
Uncut job ID: dagp7fj9k43c73admp5g
Cut averages (Z, ZZ): [-0.92305152 0.89586667]
Uncut averages (Z, ZZ): [-0.61106061 0.51606061]
Absolute cut errors: [0.07694848 0.10413333]
Absolute uncut errors: [0.38893939 0.48393939]
Total QPU usage: 102.0 seconds (1.70 minutes)
Dalam larian yang direkodkan pada ibm_boston, anggaran potongan adalah lebih kurang dan , berbanding dan untuk litar tanpa potongan. Kedua-dua anggaran potongan lebih hampir dengan nilai unggul dan .
Bandingkan setiap keputusan dengan nilai unggulnya dan periksa kedalaman litar serta jumlah penggunaan QPU. Pemotongan menghapuskan keperluan untuk melaluan gate berkala, tetapi memperkenalkan operasi perantaraan dan ketidakpastian statistik tambahan. Hingar perkakasan dan penentukuran mempengaruhi perbandingan; peningkatan tidak dijamin. Larian berulang diperlukan untuk menilai sama ada perbezaan yang diperhatikan signifikan secara statistik.
Langkah seterusnya
-
Terokai pemotongan litar untuk pengurangan kedalaman bagi interaksi tak setempat yang lain.
-
Cuba pemotongan wayar untuk anggaran nilai jangkaan untuk membahagikan litar kepada subsistem yang lebih kecil.
-
Baca bahan penerangan pemotongan litar untuk memahami pertukaran antara pemotongan dan overhead persampelan.