Pembatalan error probabilistik dengan lightcone yang diarsir
Latar Belakangβ
Tutorial ini mendemonstrasikan cara memitigasi error dengan menggunakan addon Shaded lightcone (SLC). Addon ini merupakan evolusi dari teknik probabilistic error cancellation (PEC), di mana pengguna mempelajari noise dari layer unik dalam Circuit dan kemudian membatalkan noise tersebut dengan menerapkan Gate single-qubit dan teknik post-processing. Dibandingkan metode lain, PEC menawarkan batas yang lebih kuat pada bias hasil yang dimitigasi, tetapi cenderung memiliki overhead lebih tinggi dalam hal waktu QPU. Selama PEC, untuk mengkompensasi pelemahan nilai ekspektasi oleh noise, hasil rata-rata diskalakan ulang dengan faktor , di mana adalah laju noise yang dipelajari dari Pauli error pada layer dalam Circuit. Penskalaan ulang ini meningkatkan variansi dengan faktor , dan dengan demikian juga mengalikan jumlah eksekusi Circuit yang dibutuhkan pada QPU dengan , yang kita sebut biaya sampling atau overhead sampling. Karena tumbuh secara eksponensial, PEC sering kali dibatasi pada Circuit dangkal atau Circuit dengan sedikit qubit. Pelajari lebih lanjut tentang PEC di Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors.
Jika kita bisa mengidentifikasi error yang tidak perlu dimitigasi, kita bisa mengurangi biaya sampling ini secara eksponensial. Langkah pertama dalam arah ini adalah mengimplementasikan mitigasi error yang sadar secara lokal, yang menggunakan "lightcone" konvensional yang dapat dihitung dengan cepat untuk mengurangi overhead PEC dengan membatasi sensitivitas observable terhadap error di seluruh Circuit, memperluas kelayakan PEC ke skala yang lebih besar untuk beberapa masalah. Error di luar lightcone ini tidak dapat mempengaruhi hasil pengukuran dan karenanya dapat dikecualikan dari prosedur pembatalan error. Pengecualian ini mengurangi overhead sampling, dalam beberapa kasus secara substansial, tanpa memperkenalkan bias tambahan. Khususnya, untuk mengukur observable lokal dari Circuit dengan kedalaman tetap, overhead sampling yang diperlukan akhirnya mendatar saat menskalakan jumlah qubit dalam Circuit (lihat Gbr. 2b di Locality and Error Mitigation of Quantum Circuits.)
Shaded lightcone (SLC) melangkah lebih jauh, menggunakan simulasi klasik untuk membatasi sensitivitas terhadap error di seluruh Circuit dengan lebih ketat. Ini menukar sebagian waktu QPU dengan waktu CPU dan mengurangi overhead sampling yang diperlukan untuk menormalisasi ulang bias. Alih-alih batas yang keras, setiap potensi error dalam Circuit diberi "shade" bertingkat yang membatasi kerentanan observable terhadap error tersebut. Karakterisasi yang lebih halus ini memungkinkan penerapan PEC yang lebih efisien dan terarah dengan variansi yang berkurang, sekaligus memberi pengguna kemampuan untuk mengontrol bias dalam estimasi observable secara terkendali. Lihat Lightcone shading for classically accelerated quantum error mitigation untuk detail lebih lanjut.
Alur kerja kita untuk addon SLC memanfaatkan framework Samplomatic dan Executor yang baru, memungkinkan pengguna untuk memiliki kontrol yang lebih modular atas pengaturan eksekusi untuk penekanan dan mitigasi error sambil tetap mempertahankan kemudahan penggunaan bagi pengguna tingkat lanjut. Untuk pemahaman yang lebih mendalam tentang manfaat framework ini dan fitur-fitur umumnya, lihat tutorial Hello samplomatic.
Alur kerja untuk lightcone shading, pembelajaran noise, dan injeksi anti-noiseβ
Untuk memodelkan noise QPU, kita memilih menggunakan model noise sparse Pauli-Lindblad dengan laju error Pauli 1- dan 2-qubit, yang dibuat secara lokal pada setiap qubit dan edge perangkat. Dengan pilihan ini, alur kerja mitigasi error SLC yang disajikan dalam tutorial ini adalah sebagai berikut:
a. CPU β Batasi dampak per-error dari error Pauli 1- dan 2-qubit
- Propagasi ke depan (batasi efek pada observable). Propagasikan setiap error ke akhir Circuit dan hitung komutatornya dengan observable.
- Potong suku operator selama evolusi untuk menjaga komputasi tetap dapat dikelola.
- Perketat batas ini lebih lanjut dengan propagasi balik observable yang longgar berdasarkan batas kecepatan kuantum.
- Propagasi ke belakang (batasi efek pada state awal). Propagasikan setiap error ke awal Circuit dan hitung komutatornya dengan state awal.
b. QPU β Pelajari laju noise. Gunakan NoiseLearner untuk memperkirakan laju model noise Pauli-Lindblad.
c. CPU β Prioritaskan mitigasi
- Perbarui batas yang digabungkan dengan laju noise yang dipelajari. Gabungkan batas maju dan mundur yang sebelumnya dihitung dan perbarui dengan laju noise yang dipelajari.
- Urutkan komponen noise yang akan dimitigasi menggunakan batas yang dihitung dan laju yang dipelajari. Prioritaskan setiap kemungkinan error noise berdasarkan perkiraan dampaknya terhadap bias dan biaya terkait untuk memperbaikinya.
d. QPU β Sisipkan antinoise dan jalankan. Eksekusi Circuit yang diminati dengan antinoise (inverse noise) yang ditentukan menggunakan anotasi Box.
e. CPU β Estimasi observable. Hitung nilai ekspektasi, menerapkan post-selection berbasis pengukuran untuk mengurangi dampak noise non-Markovian.
Gambaran umum pembelajaran noiseβ
Pembelajaran noise adalah langkah umum dalam beberapa metode mitigasi error, yang dilakukan oleh NoiseLearner, dan dapat dilihat dalam tutorial mitigasi error PEA kami, serta tutorial Propagated noise absorption (PNA) kami. Di NoiseLearnerV3, pengguna dapat secara spesifik mengidentifikasi layer noise yang akan dipelajari sebagai objek CircuitInstruction, yang memungkinkan pengguna untuk menghitung batas noise SLC yang diinginkan untuk setiap layer dengan cara yang dijelaskan di atas. Model Pauli-Lindblad yang dipelajari menyediakan koefisien yang akan digunakan dalam prioritisasi PEC-SLC. Cara Gate dikumpulkan ke dalam layer dapat ditentukan menggunakan fungsi kemudahan generate_boxing_pass_manager dan unique_2q_instructions, kemudian dimasukkan ke dalam fungsi utilitas SLC generate_noise_model_paulis, sebagaimana dijelaskan pada Langkah 2 di bawah.
| Bagian 1 | Bagian 2 | Bagian 3 |
|---|---|---|
| Pauli-twirl layer Gate dua-qubit | Ulangi pasangan layer identitas dan pelajari noise | Turunkan fidelitas (error untuk setiap noise channel) |
![]() | ![]() |
Gambaran umum post-processingβ
Setelah dieksekusi pada perangkat keras kuantum menggunakan framework Samplomatic dan Executor, kita mengonversi pengukuran bitstring kita menjadi nilai observable yang diinginkan. Dalam kasus Circuit Ising yang dicerminkan, kita idealnya akan mendapatkan observable yang diukur sebesar 1, karena semua qubit seharusnya kembali ke titik awal . Saat menghitung nilai observable dengan fungsi expectation_values kita, kita akan menerapkan beberapa teknik post-processing yang mengurangi dampak noise. Ini mencakup penghapusan shot yang dipengaruhi oleh noise non-Markovian, mitigasi readout-error, serta memperhitungkan detail implementasi PEC kita. Detail dibahas pada Langkah 4 di bawah.
Persyaratanβ
Sebelum memulai tutorial ini, pastikan kamu memiliki paket-paket berikut yang sudah terpasang:
- Qiskit IBM Runtime dengan primitive Executor (
pip install "qiskit-ibm-runtime @ git+https://github.com/Qiskit/qiskit-ibm-runtime.git") - Qiskit addon Shaded lightcone 0.1 (
pip install "qiskit-addon-slc~=0.1.0") - Qiskit addon utils (
pip install "qiskit-addon-utils~=0.3.0") - Samplomatic v0.16 atau lebih (
pip install samplomatic) - Dukungan Visualisasi Qiskit (
pip install "qiskit[visualization]")
Langkah 0. Pengaturanβ
Pertama, impor paket dan fungsi yang diperlukan untuk menjalankan notebook ini dengan sukses.
# Added by doQumentation β required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-slc qiskit-addon-utils qiskit-ibm-runtime samplomatic
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(module)s %(message)s")
# Setting this value prevents itertools.starmap deadlock on UNIX systems
from multiprocessing import set_start_method
set_start_method("spawn")
# Needed to prevent PySCF from parallelizing internally (SLC only)
%set_env OMP_NUM_THREADS=1
env: OMP_NUM_THREADS=1
import pickle
import numpy as np
import samplomatic
from matplotlib import pyplot as plt
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import PassManager, generate_preset_pass_manager
from qiskit_addon_slc.bounds import (
compute_backward_bounds,
compute_forward_bounds,
compute_local_scales,
merge_bounds,
tighten_with_speed_limit,
)
from qiskit_addon_slc.utils import generate_noise_model_paulis, map_modifier_ref_to_ref
from qiskit_addon_slc.visualization import draw_shaded_lightcone
from qiskit_addon_utils.exp_vals.expectation_values import executor_expectation_values
from qiskit_addon_utils.exp_vals.measurement_bases import get_measurement_bases
from qiskit_addon_utils.noise_management import gamma_from_noisy_boxes, trex_factors
from qiskit_addon_utils.noise_management.post_selection import PostSelector
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
AddPostSelectionMeasures,
AddSpectatorMeasures,
)
from qiskit_ibm_runtime import Executor, QiskitRuntimeService, QuantumProgram
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from qiskit_ibm_runtime.options import NoiseLearnerV3Options
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions
Langkah 1. Petakan masalahβ
Untuk kemudahan demonstrasi, kita memilih rantai Ising cermin 1D. Rantai Ising 1D memberikan struktur Circuit yang padat, yang praktis untuk memamerkan implementasi PEC. Circuit cermin memudahkan kita untuk mengetahui hasil yang diharapkan (yaitu, kita seharusnya mengukur observable sebesar 1).
Selanjutnya, kita ingin menjalankan Circuit cermin, sehingga untuk setiap Gate di bagian kedua Circuit, harus ada Gate invers di bagian pertama. Karena observable yang diukur memiliki pengukuran basis non-Z, dan executor memperhitungkan basis yang diinginkan di akhir Circuit, kita menyediakan fungsi prepare_basis yang menyisipkan Gate yang sesuai di awal Circuit cermin. Detail ini spesifik untuk demonstrasi Circuit cermin kita. Fungsi get_measurement_bases memungkinkan kita untuk dengan mudah mengidentifikasi Gate mana yang dibutuhkan dan di mana menambahkannya, serta melacak kehalusan indeks qubit yang muncul dari konvensi dalam anotasi box sebagaimana dibahas di bagian "Persiapkan pengukuran basis kanonik".
num_qubits = 20
target_obs_sparse = [("XZ", [6, 13], 1.0)]
observable = SparsePauliOp.from_sparse_list(target_obs_sparse, num_qubits=num_qubits)
bases_virt, reverser_virt = get_measurement_bases(observable)
num_trotter_steps = 10
rx_angle = np.pi / 4
def construct_ising_circuit(
num_qubits: int, num_trotter_steps: int, rx_angle: float, barrier: bool = True
) -> QuantumCircuit:
circuit = QuantumCircuit(num_qubits)
for _step in range(num_trotter_steps):
circuit.rx(rx_angle, range(num_qubits))
if barrier:
circuit.barrier()
for first_qubit in (1, 2):
for idx in range(first_qubit, num_qubits, 2):
# equivalent to Rzz(-pi/2):
circuit.sdg([idx - 1, idx])
circuit.cz(idx - 1, idx)
if barrier:
circuit.barrier()
return circuit
def prepare_basis(circuit: QuantumCircuit, basis: list[int]) -> QuantumCircuit:
# basis is a list of integer values from 0 to 3. These map to the basis measurement as:
# 0 = I; 1 = Z; 2 = X; 3 = Y
assert len(basis) == circuit.num_qubits
out_circ = circuit.copy_empty_like()
for qb, bas in enumerate(basis):
if bas in {0, 1}:
continue
if bas == 2:
out_circ.h(qb)
elif bas == 3:
out_circ.rx(-np.pi / 2, qb)
out_circ.barrier()
out_circ.compose(circuit, inplace=True)
return out_circ
def mirror_circuit(circuit: QuantumCircuit, *, inverse_first: bool = False) -> QuantumCircuit:
mirror_circ = circuit.copy_empty_like()
mirror_circ.compose(circuit.inverse() if inverse_first else circuit, inplace=True)
mirror_circ.barrier()
mirror_circ.compose(circuit if inverse_first else circuit.inverse(), inplace=True)
mirror_circ.measure_active()
return mirror_circ
# Instantiate circuit
circuit = construct_ising_circuit(num_qubits, num_trotter_steps, rx_angle, barrier=False)
mirrored_circuit = mirror_circuit(circuit, inverse_first=True)
mirrored_circuit = prepare_basis(mirrored_circuit, bases_virt[0])
mirrored_circuit.draw("mpl", fold=-1, scale=0.3, idle_wires=False, measure_arrows=False)

Langkah 2. Optimalkanβ
Kita akan mengoptimalkan detail yang terkait dengan Circuit yang akan dijalankan, observable yang akan diukur, dan parameter noise-learning. Sebagai titik awal, kita pastikan bahwa kita membuat instance backend dengan opsi fractional gates yang diaktifkan. Fractional gates ini akan memberikan sensitivitas yang lebih baik dalam beberapa proses penyaringan post-selection.
token = "<YOUR_TOKEN>"
instance = "<YOUR_INSTANCE>"
# This is used to retrieve shared results
shared_service = QiskitRuntimeService(
channel="ibm_quantum_platform",
token=token,
instance=instance,
)
# This is used to run on real hardware
service = service = QiskitRuntimeService()
qiskit_runtime_service._discover_account:WARNING:2025-11-10 11:19:40,108: Loading account with the given token. A saved account will not be used.
backend = service.backend("ibm_kingston", use_fractional_gates=True)
Pertama, kita akan men-transpile Circuit kita ke instruksi ISA, seperti yang diperlukan untuk eksekusi di QPU kita. Untuk data yang dikumpulkan dalam eksperimen ini, kita memilih Qubit secara manual berdasarkan evaluasi rantai berkualitas tertinggi.
layout = [44, 45, 46, 47, 57, 67, 68, 69, 78, 89, 88, 87, 97, 107, 106, 105, 104, 103, 96, 83]
isa_pm = generate_preset_pass_manager(backend=backend, initial_layout=layout, optimization_level=0)
isa_circuit = isa_pm.run(mirrored_circuit)
assert isa_circuit.layout.final_index_layout() == layout
isa_observable = observable.apply_layout(layout, num_qubits=isa_circuit.num_qubits)
2025-11-10 11:19:57,810 INFO base_tasks Pass: ContainsInstruction - 0.00715 (ms)
2025-11-10 11:19:57,811 INFO base_tasks Pass: UnitarySynthesis - 0.00525 (ms)
2025-11-10 11:19:57,811 INFO base_tasks Pass: HighLevelSynthesis - 0.02599 (ms)
2025-11-10 11:19:57,811 INFO base_tasks Pass: BasisTranslator - 0.09131 (ms)
2025-11-10 11:19:57,811 INFO base_tasks Pass: SetLayout - 0.02623 (ms)
2025-11-10 11:19:57,812 INFO base_tasks Pass: FullAncillaAllocation - 0.14400 (ms)
2025-11-10 11:19:57,812 INFO base_tasks Pass: EnlargeWithAncilla - 0.06318 (ms)
2025-11-10 11:19:57,813 INFO base_tasks Pass: ApplyLayout - 0.29802 (ms)
2025-11-10 11:19:57,813 INFO base_tasks Pass: CheckMap - 0.07820 (ms)
2025-11-10 11:19:57,814 INFO base_tasks Pass: FilterOpNodes - 0.33283 (ms)
2025-11-10 11:19:57,814 INFO base_tasks Pass: UnitarySynthesis - 0.00691 (ms)
2025-11-10 11:19:57,814 INFO base_tasks Pass: HighLevelSynthesis - 0.13208 (ms)
2025-11-10 11:19:57,816 INFO base_tasks Pass: BasisTranslator - 1.00303 (ms)
2025-11-10 11:19:57,818 INFO base_tasks Pass: FoldRzzAngle - 1.78719 (ms)
2025-11-10 11:19:57,818 INFO base_tasks Pass: ContainsInstruction - 0.00691 (ms)
2025-11-10 11:19:57,818 INFO base_tasks Pass: InstructionDurationCheck - 0.00405 (ms)
wire_order = layout + [q for q in range(isa_circuit.num_qubits) if q not in layout]
isa_circuit.draw(
"mpl", fold=-1, scale=0.3, idle_wires=False, wire_order=wire_order, measure_arrows=False
)

Box the circuitβ
Untuk kemudahan implementasi, kita akan menggunakan transpilation pass generate_boxing_pass_manager, yang menempatkan instruksi Circuit ke dalam kotak-kotak yang dianotasi. Kotak-kotak ini dengan jelas menunjukkan di mana, dalam kasus PEC, antinoise harus diinjeksikan ke dalam Circuit. Untuk detail pengaturan, lihat dokumentasi Samplomatic.
Perlu dicatat bahwa alur kerja SLC menggunakan inject_noise_strategy="individual_modification" nantinya dalam proses karena ini memungkinkan kita untuk mengidentifikasi setiap BoxOp dalam Circuit secara unik.
Fungsi find_unique_box_instructions mengiterasi Circuit yang sudah di-box dan mengidentifikasi yang memiliki layer 2Q atau pengukuran unik, untuk tujuan noise learning dan noise injection.
# Box circuit with Twirl and InjectNoise annotations
boxes_pm = generate_boxing_pass_manager(
twirling_strategy="active",
inject_noise_strategy="individual_modification",
inject_noise_targets="gates",
measure_annotations="all",
)
boxed_circuit = boxes_pm.run(isa_circuit)
# Find the unique instructions (layers) from boxed circuit
unique_2q_instructions = find_unique_box_instructions(
boxed_circuit, normalize_annotations=None, undress_boxes=True
)
2025-11-10 11:20:01,088 INFO base_tasks Pass: RemoveBarriers - 0.02289 (ms)
2025-11-10 11:20:01,100 INFO base_tasks Pass: GroupGatesIntoBoxes - 12.38990 (ms)
2025-11-10 11:20:01,101 INFO base_tasks Pass: GroupMeasIntoBoxes - 0.47898 (ms)
2025-11-10 11:20:01,104 INFO base_tasks Pass: AddTerminalRightDressedBoxes - 2.88177 (ms)
2025-11-10 11:20:01,111 INFO base_tasks Pass: AddInjectNoise - 6.66904 (ms)
boxed_circuit.draw(
"mpl", fold=-1, scale=0.3, idle_wires=False, wire_order=wire_order, measure_arrows=False
)

Siapkan pengukuran basis kanonikβ
Karena cara Qubit diberi label saat mengidentifikasi layer 2Q yang unik, kita harus berhati-hati dalam melacak urutan Qubit. Di bawah ini, kita memperkenalkan konsep canonical_qubits sebagai cara untuk memperbarui urutan Qubit secara tepat saat memberikannya ke executor, sebagai akibat dari bagaimana urutan Qubit ditangkap saat melakukan boxing pada Circuit dan menemukan instruksi unik. Lihat dokumentasi konvensi urutan Qubit untuk detailnya.
# Determine the canonical qubits order
meas_box = boxed_circuit.data[-1]
canonical_qubits = [
idx for idx, qubit in enumerate(boxed_circuit.qubits) if qubit in meas_box.qubits
]
# map canonical qubit to physical (isa) qubit
c_2_p = {c: p for c, p in enumerate(canonical_qubits)}
# map physical (isa) qubit to virtual qubit (index in original circuit)
p_2_v = {p: v for v, p in enumerate(layout)}
# compute map between virtual and canonical qubit indices.
c_2_v = {c: p_2_v[p] for c, p in c_2_p.items()}
assert len(c_2_v) == num_qubits
bases_canon = [
np.array([base_i[c_2_v[c]] for c in range(num_qubits)], dtype=np.uint8) for base_i in bases_virt
]
Alur kerja untuk shading lightcone, pembelajaran noise, dan injeksi anti-noiseβ
Catatan: Untuk implementasi SLC-PEC dalam tutorial ini, kita menjalankan komputasi batas SLC sebelum pembelajaran noise selesai, sehingga Circuit yang akan dimitigasi dijalankan sedekat mungkin waktunya dengan model noise yang telah dipelajari. Pada dasarnya, alur kerja ini bisa lebih disempurnakan agar dapat dieksekusi secara bersamaan. Yaitu, job pembelajaran noise dijalankan sementara, secara paralel, batas noise diestimasi. Untuk Circuit kuantum sembarang, komputasi batas noise bisa berskala dengan dependensi eksponensial lemah. Oleh karena itu, mungkin lebih bijaksana menggunakan eksekusi paralel saat mencoba memaksimalkan efisiensi alur kerja. Untuk tujuan ini, kita demonstrasikan secara singkat dengan menyertakan sumber daya berbasis cluster (128 thread) dan menunjukkan bagaimana kamu bisa mendapatkan sekumpulan batas yang lebih halus untuk Circuit yang diberikan ketika dibatasi pada batas waktu komputasi yang sama, dibandingkan dengan laptop kita (8 thread). Selain itu, meskipun tidak diimplementasikan dalam alur kerja ini, kamu bisa memparalelkan eksekusi QPU untuk pembelajaran noise dan komputasi batas noise agar mencapai alur kerja yang paling efisien.
Prediksi Pauli model noise yang akan dipelajariβ
Fungsi generate_noise_model_paulis menelusuri setiap lapisan boxed dari Circuit yang diberikan dan menghasilkan semua suku Pauli relevan berbobot satu dan berbobot dua, dengan mempertimbangkan konektivitas Qubit Circuit, dan memilih suku-suku yang relevan dengan node dan edge aktif. Suku-suku ini kemudian digunakan untuk menghitung batas noise maju dan mundur.
noise_model_paulis = generate_noise_model_paulis(
unique_2q_instructions, backend.coupling_map, boxed_circuit
)
noise_model_rates = {ref: None for ref in noise_model_paulis}
a. Hitung dan perketat batas majuβ
Fungsi compute_forward_bounds mengevaluasi relasi komutasi antara Gate di setiap lapisan dan suku-suku Pauli yang dihasilkan di atas dalam hal bagaimana error propagasi maju memengaruhi observable yang diinginkan . Untuk Gate yang komut dengan suku Pauli, tidak ada yang dilakukan. Untuk Gate Clifford, mereka didorong ke awal Circuit. Untuk Gate non-Clifford, kita mengaproksimasi pengaruhnya pada observable target agar diprioritaskan untuk pembatalan noise (setelah semua batas digabungkan). Batas ini dicapai dengan pertama menerapkan norma L2 (yaitu, akar kuadrat dari jumlah kuadrat koefisien suku Pauli yang relevan). Ketika ada terlalu banyak suku Qubit yang terlibat, kita kembali ke batas yang lebih longgar menggunakan ketidaksetaraan segitiga.
Sumber daya tingkat laptopβ
slc_atol = 1e-8
slc_eigval_max_qubits = 18
slc_evolution_max_terms = 1000
slc_num_processes = 8
slc_timeout = 60
forward_bounds = compute_forward_bounds(
boxed_circuit,
noise_model_paulis,
isa_observable,
evolution_max_terms=slc_evolution_max_terms,
eigval_max_qubits=slc_eigval_max_qubits,
atol=slc_atol,
num_processes=slc_num_processes,
timeout=slc_timeout,
)
2025-11-10 11:20:04,344 INFO forward Evolving Pauli error terms forwards through the circuit.
2025-11-10 11:20:04,344 INFO forward Modelling errors as though they happen *after* each noise layer.
2025-11-10 11:20:04,345 INFO remove_measure Removing ANY Measure operations from the provided circuit!
2025-11-10 11:20:04,453 INFO circuit_iter Noisy box 'm39'
2025-11-10 11:20:05,254 INFO circuit_iter Noisy box 'm38'
2025-11-10 11:20:05,304 INFO circuit_iter Noisy box 'm37'
2025-11-10 11:20:05,382 INFO circuit_iter Noisy box 'm36'
2025-11-10 11:20:05,467 INFO circuit_iter Noisy box 'm35'
2025-11-10 11:20:05,580 INFO circuit_iter Noisy box 'm34'
2025-11-10 11:20:05,705 INFO circuit_iter Noisy box 'm33'
2025-11-10 11:20:05,857 INFO circuit_iter Noisy box 'm32'
2025-11-10 11:20:06,034 INFO circuit_iter Noisy box 'm31'
2025-11-10 11:20:06,221 INFO circuit_iter Noisy box 'm30'
2025-11-10 11:20:06,449 INFO circuit_iter Noisy box 'm29'
2025-11-10 11:20:06,724 INFO circuit_iter Noisy box 'm28'
2025-11-10 11:20:07,628 INFO circuit_iter Noisy box 'm27'
2025-11-10 11:20:09,110 INFO circuit_iter Noisy box 'm26'
2025-11-10 11:20:11,696 INFO circuit_iter Noisy box 'm25'
2025-11-10 11:20:16,100 INFO circuit_iter Noisy box 'm24'
2025-11-10 11:20:21,781 INFO circuit_iter Noisy box 'm23'
2025-11-10 11:20:30,244 INFO circuit_iter Noisy box 'm22'
2025-11-10 11:20:40,416 INFO circuit_iter Noisy box 'm21'
2025-11-10 11:20:53,437 INFO circuit_iter Noisy box 'm20'
2025-11-10 11:21:06,038 INFO circuit_iter Noisy box 'm19'
2025-11-10 11:21:06,038 WARNING commutator_bounds Bounds computation timed out.
2025-11-10 11:21:06,039 INFO circuit_iter Noisy box 'm18'
2025-11-10 11:21:06,039 INFO circuit_iter Noisy box 'm17'
2025-11-10 11:21:06,039 INFO circuit_iter Noisy box 'm16'
2025-11-10 11:21:06,040 INFO circuit_iter Noisy box 'm15'
2025-11-10 11:21:06,040 INFO circuit_iter Noisy box 'm14'
2025-11-10 11:21:06,040 INFO circuit_iter Noisy box 'm13'
2025-11-10 11:21:06,040 INFO circuit_iter Noisy box 'm12'
2025-11-10 11:21:06,041 INFO circuit_iter Noisy box 'm11'
2025-11-10 11:21:06,041 INFO circuit_iter Noisy box 'm10'
2025-11-10 11:21:06,041 INFO circuit_iter Noisy box 'm9'
2025-11-10 11:21:06,042 INFO circuit_iter Noisy box 'm8'
2025-11-10 11:21:06,042 INFO circuit_iter Noisy box 'm7'
2025-11-10 11:21:06,042 INFO circuit_iter Noisy box 'm6'
2025-11-10 11:21:06,042 INFO circuit_iter Noisy box 'm5'
2025-11-10 11:21:06,043 INFO circuit_iter Noisy box 'm4'
2025-11-10 11:21:06,043 INFO circuit_iter Noisy box 'm3'
2025-11-10 11:21:06,043 INFO circuit_iter Noisy box 'm2'
2025-11-10 11:21:06,043 INFO circuit_iter Noisy box 'm1'
2025-11-10 11:21:06,044 INFO circuit_iter Noisy box 'm0'
Visualisasi SLC untuk inspeksi manualβ
Kamu bisa menginterpretasikan perilaku batas yang diarsir dengan memeriksa bagaimana pengukuran dan suku Pauli berinteraksi dengan error lokal. Pola-pola ini merupakan karakteristik dari masalah evolusi waktu Hamiltonian Ising yang ditendang ini dan juga muncul dalam paper Lightcone Shading for Classically Accelerated Quantum Error Mitigation, dengan beberapa fitur khas:
- Kita dapat dengan jelas membedakan dua kerucut yang muncul dari dua Pauli non-identitas dalam observable.
- Kita dapat melihat bahwa pengukuran X pada Qubit 6 komut dengan error X di lapisan paling kanan.
- Kita dapat melihat bahwa Pauli Z pada Qubit 13 komut dengan error Z di lapisan paling kanan.
- Ketika kita mencapai timeout yang ditentukan di atas, lapisan-lapisan yang tersisa di sebelah kiri diisi seluruhnya dengan batas trivial sebesar dua.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
forward_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



b. Hitung dan perketat batas majuβ
Selanjutnya kita memperketat batas dengan menggunakan fungsi tighten_with_speed_limit, yang melacak bagaimana observable menyebar mundur melalui Circuit dan menggunakan penyebaran tersebut untuk menetapkan batas atas pada efek setiap operator noise, mengambil yang lebih kecil antara batas maju yang baru dihitung, dan batas propagasi mundur.
forward_bounds_tighter = tighten_with_speed_limit(
forward_bounds, boxed_circuit, noise_model_paulis, isa_observable
)
2025-11-10 11:21:08,270 INFO speed_limit Tighting bounds using information propagation speed limits
2025-11-10 11:21:08,270 INFO speed_limit Modelling errors as though they happen *after* each noise layer.
2025-11-10 11:21:08,298 INFO remove_measure Removing ANY Measure operations from the provided circuit!
2025-11-10 11:21:08,310 INFO circuit_iter Noisy box 'm39'
2025-11-10 11:21:08,314 INFO circuit_iter Noisy box 'm38'
2025-11-10 11:21:08,317 INFO circuit_iter Noisy box 'm37'
2025-11-10 11:21:08,319 INFO circuit_iter Noisy box 'm36'
2025-11-10 11:21:08,323 INFO circuit_iter Noisy box 'm35'
2025-11-10 11:21:08,325 INFO circuit_iter Noisy box 'm34'
2025-11-10 11:21:08,328 INFO circuit_iter Noisy box 'm33'
2025-11-10 11:21:08,330 INFO circuit_iter Noisy box 'm32'
2025-11-10 11:21:08,334 INFO circuit_iter Noisy box 'm31'
2025-11-10 11:21:08,336 INFO circuit_iter Noisy box 'm30'
2025-11-10 11:21:08,338 INFO circuit_iter Noisy box 'm29'
2025-11-10 11:21:08,340 INFO circuit_iter Noisy box 'm28'
2025-11-10 11:21:08,344 INFO circuit_iter Noisy box 'm27'
2025-11-10 11:21:08,346 INFO circuit_iter Noisy box 'm26'
2025-11-10 11:21:08,349 INFO circuit_iter Noisy box 'm25'
2025-11-10 11:21:08,351 INFO circuit_iter Noisy box 'm24'
2025-11-10 11:21:08,355 INFO circuit_iter Noisy box 'm23'
2025-11-10 11:21:08,357 INFO circuit_iter Noisy box 'm22'
2025-11-10 11:21:08,360 INFO circuit_iter Noisy box 'm21'
2025-11-10 11:21:08,362 INFO circuit_iter Noisy box 'm20'
2025-11-10 11:21:08,367 INFO circuit_iter Noisy box 'm19'
2025-11-10 11:21:08,369 INFO circuit_iter Noisy box 'm18'
2025-11-10 11:21:08,372 INFO circuit_iter Noisy box 'm17'
2025-11-10 11:21:08,375 INFO circuit_iter Noisy box 'm16'
2025-11-10 11:21:08,378 INFO circuit_iter Noisy box 'm15'
2025-11-10 11:21:08,380 INFO circuit_iter Noisy box 'm14'
2025-11-10 11:21:08,383 INFO circuit_iter Noisy box 'm13'
2025-11-10 11:21:08,386 INFO circuit_iter Noisy box 'm12'
2025-11-10 11:21:08,389 INFO circuit_iter Noisy box 'm11'
2025-11-10 11:21:08,391 INFO circuit_iter Noisy box 'm10'
2025-11-10 11:21:08,394 INFO circuit_iter Noisy box 'm9'
2025-11-10 11:21:08,396 INFO circuit_iter Noisy box 'm8'
2025-11-10 11:21:08,399 INFO circuit_iter Noisy box 'm7'
2025-11-10 11:21:08,401 INFO circuit_iter Noisy box 'm6'
2025-11-10 11:21:08,404 INFO circuit_iter Noisy box 'm5'
2025-11-10 11:21:08,406 INFO circuit_iter Noisy box 'm4'
2025-11-10 11:21:08,410 INFO circuit_iter Noisy box 'm3'
2025-11-10 11:21:08,412 INFO circuit_iter Noisy box 'm2'
2025-11-10 11:21:08,415 INFO circuit_iter Noisy box 'm1'
2025-11-10 11:21:08,417 INFO circuit_iter Noisy box 'm0'
Visualisasi SLC untuk inspeksi manualβ
Kita bisa lebih memperketat batas dengan memperhitungkan keterbatasan lightcone. Pada dasarnya, ini memberi kita transisi yang lebih mulus dari batas yang dihitung ke batas trivial yang ditetapkan setelah timeout tercapai. Di sini, efeknya tidak terlalu terlihat karena lightcone sudah mencapai tepi Circuit.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
forward_bounds_tighter,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



c. Hitung batas mundurβ
Bagian prediksi noise ini mengevaluasi bagaimana error pada lapisan tertentu dapat memengaruhi state input . Fungsi compute_backward_bounds pertama-tama membalik Circuit, menghapus Gate pengukuran, lalu melanjutkan dengan analisis yang serupa seperti yang dilakukan untuk komputasi batas maju.
backward_bounds = compute_backward_bounds(
boxed_circuit,
noise_model_paulis,
evolution_max_terms=slc_evolution_max_terms,
num_processes=slc_num_processes,
timeout=slc_timeout,
)
2025-11-10 11:21:10,666 INFO backward Evolving Pauli error terms backwards through the circuit.
2025-11-10 11:21:10,666 INFO backward Modelling errors as though they happen *after* each noise layer.
2025-11-10 11:21:10,667 INFO remove_measure Removing ANY Measure operations from the provided circuit!
2025-11-10 11:21:10,774 INFO circuit_iter Noisy box 'm0'
2025-11-10 11:21:11,640 INFO circuit_iter Noisy box 'm1'
2025-11-10 11:21:11,681 INFO circuit_iter Noisy box 'm2'
2025-11-10 11:21:11,867 INFO circuit_iter Noisy box 'm3'
2025-11-10 11:21:12,078 INFO circuit_iter Noisy box 'm4'
2025-11-10 11:21:12,329 INFO circuit_iter Noisy box 'm5'
2025-11-10 11:21:12,637 INFO circuit_iter Noisy box 'm6'
2025-11-10 11:21:13,110 INFO circuit_iter Noisy box 'm7'
2025-11-10 11:21:13,705 INFO circuit_iter Noisy box 'm8'
2025-11-10 11:21:14,384 INFO circuit_iter Noisy box 'm9'
2025-11-10 11:21:15,213 INFO circuit_iter Noisy box 'm10'
2025-11-10 11:21:15,946 INFO circuit_iter Noisy box 'm11'
2025-11-10 11:21:16,754 INFO circuit_iter Noisy box 'm12'
2025-11-10 11:21:17,557 INFO circuit_iter Noisy box 'm13'
2025-11-10 11:21:18,447 INFO circuit_iter Noisy box 'm14'
2025-11-10 11:21:19,453 INFO circuit_iter Noisy box 'm15'
2025-11-10 11:21:20,472 INFO circuit_iter Noisy box 'm16'
2025-11-10 11:21:21,479 INFO circuit_iter Noisy box 'm17'
2025-11-10 11:21:22,660 INFO circuit_iter Noisy box 'm18'
2025-11-10 11:21:23,705 INFO circuit_iter Noisy box 'm19'
2025-11-10 11:21:24,849 INFO circuit_iter Noisy box 'm20'
2025-11-10 11:21:26,030 INFO circuit_iter Noisy box 'm21'
2025-11-10 11:21:27,111 INFO circuit_iter Noisy box 'm22'
2025-11-10 11:21:28,354 INFO circuit_iter Noisy box 'm23'
2025-11-10 11:21:29,554 INFO circuit_iter Noisy box 'm24'
2025-11-10 11:21:30,897 INFO circuit_iter Noisy box 'm25'
2025-11-10 11:21:32,113 INFO circuit_iter Noisy box 'm26'
2025-11-10 11:21:33,622 INFO circuit_iter Noisy box 'm27'
2025-11-10 11:21:34,962 INFO circuit_iter Noisy box 'm28'
2025-11-10 11:21:36,504 INFO circuit_iter Noisy box 'm29'
2025-11-10 11:21:38,021 INFO circuit_iter Noisy box 'm30'
2025-11-10 11:21:39,750 INFO circuit_iter Noisy box 'm31'
2025-11-10 11:21:41,237 INFO circuit_iter Noisy box 'm32'
2025-11-10 11:21:42,974 INFO circuit_iter Noisy box 'm33'
2025-11-10 11:21:44,527 INFO circuit_iter Noisy box 'm34'
2025-11-10 11:21:46,535 INFO circuit_iter Noisy box 'm35'
2025-11-10 11:21:48,152 INFO circuit_iter Noisy box 'm36'
2025-11-10 11:21:50,074 INFO circuit_iter Noisy box 'm37'
2025-11-10 11:21:51,814 INFO circuit_iter Noisy box 'm38'
2025-11-10 11:21:53,943 INFO circuit_iter Noisy box 'm39'
Visualisasi SLC untuk inspeksi manualβ
Dari komputasi batas mundur, kita dapat melihat bagaimana struktur state awal mengatur perilaku awal propagasi error:
- Kita dapat dengan jelas melihat bagaimana error Z awalnya komut dengan state awal |0β©.
- Hanya pada Qubit 6, tempat kita menginisialisasi eigenstate +1 dari basis X, error Z gagal untuk komut, sementara error X justru komut.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
backward_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



Pratinjau batas yang digabungkan tanpa tingkat noise yang dipelajariβ
Fungsi merged_bounds menentukan titik dalam Circuit di mana beralih dari batas mundur ke batas maju meminimalkan total estimasi bias pada observable yang diinginkan. Bias ini dihitung sebagai jumlah kontribusi batas mundur untuk semua lokasi noise sebelum titik tersebut, ditambah kontribusi batas maju untuk semua lokasi noise setelahnya. Saat ini, ini dilakukan secara seragam untuk semua Qubit.
Catatan Penting: Titik untuk beralih dari batas maju ke batas mundur bergantung pada tingkat noise yang dipelajari.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
noise_model_rates,
)
2025-11-10 11:21:58,304 WARNING merge Missing noise rates. Partitioning backward/forward commutator bounds by assuming uniform error rates.
2025-11-10 11:21:58,305 WARNING merge Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
2025-11-10 11:21:58,305 INFO merge Determined Box idx for partitioning to be 20.
Visualisasi SLC untuk inspeksi manualβ
Setelah menggabungkan batas mundur dan batas maju yang diperketat, perilaku SLC gabungan menjadi jelas:
- Fungsi di atas memberi tahu kita bahwa sebuah partisi dipilih di mana peralihan dari batas mundur ke batas maju yang diperketat terjadi.
- Kita dapat melihat di bawah bahwa SLC sekarang mengandung batas mundur sebagian dan batas maju yang diperketat sebagian.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
merged_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



Sumber daya tingkat clusterβ
Di sini, kita mendemonstrasikan bagaimana penggunaan 128 thread pada cluster memungkinkan kita untuk mempropagasi melalui bagian yang lebih substansial dari Circuit yang lebih besar ini ketika dibatasi pada waktu komputasi yang sama seperti laptop kita.
with open("exp_data/merged_bounds_cluster.pickle", "rb") as file:
merged_bounds_cluster = pickle.load(file)
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
merged_bounds_cluster,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



Langkah 3. Eksekusiβ
Di bagian ini kita mulai masuk ke tahap workflow yang menggunakan perangkat quantum nyata. Untuk metode mitigasi error berbasis pembelajaran ini, ada dua langkah yang perlu dilakukan:
- Pelajari noise dengan menggunakan
NoiseLeanerV3. - Jalankan circuit mitigasi error dengan framework Samplomatic dan Estimator yang baru.
Dengan error yang sudah dibatasi dari circuit quantum kita, kita perlu mempelajari tingkat noise yang terkait untuk memprioritaskan anggaran error, menentukan overhead sampling, dan mengeksekusi di QPU. Selain itu, dengan informasi tingkat noise ini, kita juga bisa melihat bagaimana penggunaan sumber daya komputasi yang kuat dari cluster kita dapat mengurangi overhead sampling sekaligus mempertahankan bias residual yang sama.
a. Pelajari tingkat noiseβ
Noise learner memungkinkan karakterisasi proses noise yang mempengaruhi gate dalam satu atau lebih circuit yang diminati, berdasarkan model noise Pauli-Lindblad yang dijelaskan dalam makalah Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors. Metode run() meluncurkan job pembelajaran noise untuk lapisan 2-qubit unik yang diberikan, berdasarkan opsi yang ditentukan dalam konfigurasi noise-learner. Dalam opsi ini, kamu bisa menyesuaikan strategi Pauli-twirling, yang membantu memastikan hardware terdeskripsi dengan baik oleh model noise Pauli-Lindblad.
Detail model noise kamu berisiko mengalami pergeseran seiring waktu. Oleh karena itu, kita menetapkan parameter untuk memastikan model noise yang sudah dipelajari dihitung ulang untuk eksperimen yang lebih dari empat jam. Ini adalah aturan praktis yang kasar dan harus dipertimbangkan dengan cermat saat menerapkannya pada pekerjaan kamu sendiri.
post_selection_enabled = True
load_cached_noise_results = True
noise_learner_options = NoiseLearnerV3Options(
num_randomizations=64,
shots_per_randomization=128,
layer_pair_depths=[1, 2, 4, 8, 12, 16, 24, 32, 40, 48],
post_selection={
"enable": post_selection_enabled,
"strategy": "edge",
"x_pulse_type": "rx",
},
)
noise_learner = NoiseLearnerV3(backend, noise_learner_options)
if load_cached_noise_results:
noise_learner_job = shared_service.job("d46ssf71gh7s7398k9a0")
else:
noise_learner_job = noise_learner.run(unique_2q_instructions)
noise_learner_result = noise_learner_job.result()
if post_selection_enabled:
print("Minimum fraction of shots kept for noise learning experiments: ", end="")
print(
f"{min([min(d.values()) for d in [nlr.metadata['post_selection']['fraction_kept'] for nlr in noise_learner_result[:2]]]):.2f}"
)
Minimum fraction of shots kept for noise learning experiments: 0.58
# Get a dict mapping InjectNoise.ref to QubitSparsePaulilist
refs_2_plm = noise_learner_result.to_dict(unique_2q_instructions, require_refs=False)
b.i. Perbarui batas gabungan dengan tingkat noise yang sudah dipelajariβ
Sekarang model noise spesifik sudah dipelajari, kita bisa menerapkan tingkat noise yang dipelajari ke batas noise yang diprediksi dan mendapatkan penentuan akhir tentang batas mana yang paling berpengaruh dalam meminimalkan bias.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
refs_2_plm,
)
2025-11-10 11:22:03,755 WARNING merge Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
2025-11-10 11:22:03,756 INFO merge Determined Box idx for partitioning to be 20.
b.ii. Hitung local_scales untuk eksekusi hardwareβ
compute_local_scales melihat setiap kemungkinan error noise dalam circuit dan memperkirakan seberapa besar error tersebut bisa menyebabkan bias pada pengukuran akhir, serta seberapa mahal biaya untuk mengoreksinya. Kemudian ia mengurutkan error berdasarkan seberapa layak untuk dimitigasi dan memilih subset yang mengurangi bias sebanyak mungkin, sambil tetap dalam anggaran biaya sampling yang diizinkan (atau mencapai akurasi yang diinginkan). Hasilnya adalah sekumpulan faktor skala yang menunjukkan error mana yang akan dimitigasi secara aktif dan mana yang dibiarkan tidak dimitigasi (local_scales), beserta prediksi total overhead biaya sampling (sampling_costs) dan bias residual yang tersisa (residual_bias_bound).
Kemampuan untuk mengontrol bias residual yang diinginkan adalah fitur kritis dari implementasi SLC untuk PEC. Berbeda dengan implementasi aslinya yang overhead sampling-nya selalu menargetkan nol bias, kita bisa menyesuaikan overhead sampling yang diperlukan dengan trade-off pada bias residual yang diharapkan. Ini membantu pengguna tetap dalam anggaran sampling yang tetap, yang bisa sangat berguna saat awalnya melakukan prototyping sebuah workflow.
id_map = map_modifier_ref_to_ref(boxed_circuit)
summed_rates = 0.0
for _box_id, noise_id in id_map.items():
learned_plm = refs_2_plm[noise_id]
summed_rates += np.sum(learned_plm.rates)
# print(f"{_box_id}:\tgamma = {np.exp(2 * summed_rates):1.6e}\tsampling cost = {np.exp(4 * summed_rates):1.6e}")
total_gamma = np.exp(2 * summed_rates)
print(f"Full PEC gamma={total_gamma}, sampling cost (gamma^2) = {total_gamma**2}")
Full PEC gamma=128.56055005423153, sampling cost (gamma^2) = 16527.81503024657
biases = []
costs = []
for bias in [0.0, *np.arange(0.001, 0.102, 0.01).tolist()]:
_, cost_, bias_ = compute_local_scales(
boxed_circuit,
merged_bounds,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=bias,
)
biases.append(bias_)
costs.append(cost_)
biases_cluster = []
costs_cluster = []
for bias in [0.0, *np.arange(0.001, 0.102, 0.01).tolist()]:
_, cost_, bias_ = compute_local_scales(
boxed_circuit,
merged_bounds_cluster,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=bias,
)
biases_cluster.append(bias_)
costs_cluster.append(cost_)
Manfaat cluster untuk mengurangi overhead sampling pada waktu komputasi klasik tertentuβ
xticks = np.arange(0, 11)
fig, ax = plt.subplots()
ax.scatter([0], [total_gamma**2], marker="D", c="tab:orange", label="full PEC")
ax.plot(100 * np.array(biases), np.array(costs), "o-", c="tab:blue", label="local PEC+SLC")
ax.plot(
100 * np.array(biases_cluster),
np.array(costs_cluster),
"o-",
c="tab:green",
label="cluster PEC+SLC",
)
ax.set_yscale("log")
ax.set_ylim([100, 50000])
ax.set_xticks(xticks, [f"{x:.1f}" for x in xticks])
ax.set_xlabel("Remaining Bias [%]")
ax.set_ylabel(r"Sampling Overhead, $\gamma^2$")
ax.grid()
ax.legend()
fig.suptitle("PEC sampling overhead reduction due to SLC")
Text(0.5, 0.98, 'PEC sampling overhead reduction due to SLC')

chosen_bias_thres = 0.1
local_scales, sampling_cost, residual_bias_bound = compute_local_scales(
boxed_circuit,
merged_bounds_cluster,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=chosen_bias_thres,
)
print(
f"PEC+SLC sampling cost (gamma^2) = {sampling_cost} w/ remaining bias = {100 * residual_bias_bound:.1f}%"
)
PEC+SLC sampling cost (gamma^2) = 563.1803982530477 w/ remaining bias = 9.3%
c. Jalankan circuit yang diinginkan dengan antinoiseβ
c.i. Siapkan template circuit menggunakan samplexβ
samplex adalah output dari metode build milik Samplomatic, yang mengenkode semua informasi yang diperlukan untuk menghasilkan parameter teracak bagi template_circuit. Parameter-parameter ini kemudian digunakan untuk menyiapkan objek QuantumProgram, yang selanjutnya dijalankan di QPU menggunakan primitive Executor. Setiap QuantumProgram bisa berisi beberapa item, yang bisa kamu bayangkan sebagai pasangan template dan samplex.
Lihat tutorial Hello samplomatic untuk detailnya.
# Build template circuit and samplex for later use with the "Executor"
template_circuit, samplex = samplomatic.build(boxed_circuit)
# Set up postselection if it's been enabled
if post_selection_enabled:
# Set up post selection PM (to add PS instructions)
post_selection_pm = PassManager(
[
AddSpectatorMeasures(backend.coupling_map),
AddPostSelectionMeasures(x_pulse_type="rx"),
]
)
final_template_circuit = post_selection_pm.run(template_circuit)
else:
final_template_circuit = template_circuit
2025-11-10 11:22:04,839 INFO base_tasks Pass: AddSpectatorMeasures - 3.41392 (ms)
2025-11-10 11:22:04,843 INFO base_tasks Pass: AddPostSelectionMeasures - 2.88510 (ms)
c.ii. Siapkan QuantumProgramβ
num_randomizations = 4096
shots_per_randomization = 64
chunk_size = 256
# Set up QuantumProgram
program = QuantumProgram(shots=shots_per_randomization, noise_maps=refs_2_plm)
# no EM
# Collect up a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {f"noise_scales.{ref}": float(0) for ref in local_scales}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = samplex.inputs().bind(**samplex_inputs).make_broadcastable()
program.append(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
# plain PEC
# Collect a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {f"noise_scales.{ref}": float(-1) for ref in local_scales}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = samplex.inputs().bind(**samplex_inputs).make_broadcastable()
program.append(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
# PEC+SLC
# Collect a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {f"noise_scales.{ref}": float(-1) for ref in local_scales}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
samplex_inputs |= {"local_scales": local_scales}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = samplex.inputs().bind(**samplex_inputs).make_broadcastable()
program.append(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
c.iii. Jalankan program dengan primitive Executorβ
executor = Executor(backend)
load_cached_executor_results = True
if load_cached_executor_results:
job_exec = shared_service.job("d46t1q6qsa9s73cb28g0")
else:
job_exec = executor.run(program)
results_exec = job_exec.result()
Langkah 4. Post-processβ
Saat menghitung nilai ekspektasi akhir yang kita inginkan menggunakan expectation_values, kita akan menerapkan beberapa teknik post-processing yang bermanfaat untuk memastikan kita mendapatkan hasil berkualitas terbaik. Pertama, kita terapkan twirled readout mitigation, TREX, yang menangani error yang terjadi selama proses readout. Kemudian, kita perbaiki error akibat noise non-Markovian pada backend Heron kita menggunakan metode post-selection. Metode ini mengukur qubit aktif dan spektator, lalu menerapkan rotasi lambat ke setiap qubit, kemudian mengukur lagi. Dalam kasus di mana dua pengukuran tidak mengkonfirmasi qubit yang terbalik seperti yang diharapkan, shot tersebut dibuang dengan menerapkan mask dari fungsi PostSelector. Dalam komputasi mask, strategi tertentu bisa diatur untuk menyaring berdasarkan node qubit tunggal atau edge spektator tetangga, yang bisa mempengaruhi jumlah shot yang disaring maupun kualitas hasil.
measurement_noise_map = noise_learner_result[2].to_pauli_lindblad_map()
trex_scale_factors = trex_factors(measurement_noise_map, reverser_virt)
post_selection_strategy = "node"
def post_process_conv(datum, steps=16, gamma=None, ps=False, trex=False):
meas = datum["meas"]
flips = datum["measurement_flips.meas"]
signs = datum.get("pauli_signs", None)
meas_basis_axis = None
avg_axis = 0
mask = None
if ps and post_selection_enabled:
# Post-select the results
post_selector = PostSelector.from_circuit(
circuit=final_template_circuit, coupling_map=backend.coupling_map
)
# Compute the ps mask for filtering results
mask = post_selector.compute_mask(datum, strategy=post_selection_strategy)
# Compute fraction of shots kept from post selection
total_num_shots = num_randomizations * shots_per_randomization
ps_ratio = np.sum(mask) * 100 / total_num_shots / len(bases_canon)
print(
f"With {post_selection_strategy}-based post selection ({ps_ratio:.1f}% of shots kept):"
)
results = []
for i in range(steps, num_randomizations + 1, steps):
# Compute mitigated expvals w/out postselectoion
res = executor_expectation_values(
meas[:i],
reverser_virt,
meas_basis_axis,
avg_axis=avg_axis,
measurement_flips=flips[:i],
pauli_signs=signs[:i] if signs is not None else None,
postselect_mask=mask[:i] if mask is not None else None,
rescale_factors=trex_scale_factors if trex else None,
gamma_factor=gamma,
)
results.append(res[0])
return results
gamma_pec = gamma_from_noisy_boxes(refs_2_plm, id_map)
gamma_slc = gamma_from_noisy_boxes(refs_2_plm, id_map, local_scales)
steps = 16
results = {}
for label, result_idx, gamma, use_ps, use_trex in [
("PEC", 1, gamma_pec, True, True),
("PEC+SLC", 2, gamma_slc, True, True),
("Unmitigated", 0, None, False, False),
]:
res = post_process_conv(
results_exec[result_idx], steps=steps, gamma=gamma, ps=use_ps, trex=use_trex
)
results[label] = res
With node-based post selection (27.0% of shots kept):
With node-based post selection (26.8% of shots kept):
Dari pemeriksaan hasil eksperimen, kita bisa langsung membandingkan perilaku berbagai pendekatan: PEC, PEC dikombinasikan dengan SLC, dan baseline hasil yang tidak dimitigasi. Beberapa detail spesifik yang perlu disorot:
- Hasil yang tidak dimitigasi tetap berada di luar rentang bias yang diinginkan dan tidak terpengaruh oleh overhead sampling.
- Mengingat biaya sampling yang tinggi yang dihitung di atas (~10k), PEC saja tidak konvergen dalam batas randomisasi yang digunakan.
- PEC + SLC, sebaliknya, konvergen jauh lebih cepat.
- Batas error juga menurun jauh lebih cepat untuk PEC + SLC dibandingkan PEC biasa.
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
ax.axhline(1.0, color="black", label="Exact")
ax.fill_between([-50, 4100], -10, 0, color="grey", alpha=0.25, label="Unphysical")
ax.fill_between([-50, 4100], 1, 10, color="grey", alpha=0.25)
ax.fill_between([-50, 4100], 0.9, 1.1, color="red", alpha=0.25, label="10% bias")
for label, res in results.items():
ax.errorbar(
list(range(steps, num_randomizations + 1, steps)),
[r[0] for r in res],
yerr=[r[1] for r in res],
alpha=0.75,
marker="o",
linestyle="",
markerfacecolor="none",
label=label,
)
ax.set_ylabel(r"$\langle X_{6}Z_{13}\rangle$")
ax.set_xlabel("# randomizations")
ax.grid()
ax.legend(ncols=2)
ax.set_ylim([-0.1, 2.0])
ax.set_xlim([-50, 4100])
(-50.0, 4100.0)


