Probabilistic error cancellation na may shaded lightcones
Tinatayang paggamit: 10 minuto sa isang Heron processor (TANDAAN: Ito ay pagtantya lamang. Maaaring magbago ang runtime mo.)
Mga natutunan
-
Ano ang probabilistic error cancellation (PEC), at kung bakit lumalaki nang exponential ang sampling overhead nito na kasabay ng kabuuang noise na kumikilos sa circuit
-
Paano nagbibigay-hangganan ang shaded lightcones (SLC) sa kontribusyon ng bawat noise term sa target na observable, upang magamit mo ang badyet ng mitigation kung saan ito mahalaga — at kung paano, higit pa sa zero-bias na pagbawas ng overhead na ibinibigay na ng puro geometric lightcone, pinapayagan ng shading na ipagpalit ang isang nakahangganang natitirang bias para sa mas mababang sampling overhead
-
Paano mag-aral ng layer noise gamit ang
NoiseLearnerV3at mag-iniksyon ng anti-noise sa pamamagitan ngsamplomaticat ngExecutorprimitive -
Paano pagsamahin ang PEC at PEC+SLC kasama ang TREX at postselection upang tantiyahin ang isang expectation value sa hardware
Mga paunang kailangan
-
Ang Qiskit patterns workflow
-
Paggamit ng Estimator primitive upang kalkulahin ang expectation values ng isang observable
-
Mga teknik ng error mitigation tulad ng Pauli twirling at TREX, na sinasaklaw sa Pagsasama ng mga opsyon ng error mitigation sa Estimator primitive
Background
Ipinapakita ng tutorial na ito kung paano pagaanin ang mga error gamit ang shaded lightcone (SLC) addon. Ang addon na ito ay isang ebolusyon ng probabilistic error cancellation (PEC) technique, kung saan natututo ang user ng noise ng mga natatanging layer sa isang circuit at pagkatapos ay kina-cancel ang noise sa pamamagitan ng paglalapat ng single-qubit gates at post-processing techniques. Kung ikukumpara sa ibang mga pamamaraan, ang PEC ay nag-aalok ng mas matatag na hangganan sa bias ng naitamang resulta, ngunit malamang na dumaranas ng mas mataas na overhead sa termino ng QPU time. Sa panahon ng PEC, upang mabawi ang pag-atenwas ng expectation value ng noise, ang average result ay na-rescale ng salik na , kung saan ang ay ang natutunang noise rate ng error Pauli na sa layer na sa circuit. Ang rescaling na ito ay nagpapataas ng variance nang salik na , at sa gayon ay pinaparami rin ang bilang ng circuit executions na kailangan sa QPU nang , na tinatawag natin na sampling cost o sampling overhead. Dahil lumalaki nang exponential ang , ang PEC ay madalas limitado sa mababaw o kakaunting-qubit na circuits. Alamin pa ang tungkol sa PEC sa Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors.
Kung matukoy natin ang mga error na hindi kailangang pagaanin, mababawasan natin ang sampling cost na ito nang exponential. Isang unang hakbang sa direksyong ito ay ang pagpapatupad ng locally-aware error mitigation, na gumagamit ng mabilis-makalkulang conventional na "lightcone" upang bawasan ang PEC overhead sa pamamagitan ng paglilimita sa sensitivity ng isang observable sa mga error sa buong circuit, na nagpapalawak ng feasibility ng PEC sa mas malalaking scale para sa ilang problema. Ang mga error sa labas ng lightcone na ito ay hindi maaaring makaapekto sa nasusukat na kinalabasan at samakatuwid ay maaaring ibukod mula sa error cancellation procedure. Ang pagbubukod na ito ay nagpapababa ng sampling overhead, sa ilang kaso nang malaki, nang hindi nagpapakilala ng karagdagang bias. Sa partikular, para sa pagsukat ng lokal na observable na ng isang fixed-depth circuit, ang kinakailangang sampling overhead ay unti-unting nagpaplateau habang pinapalaki ang bilang ng qubits sa circuit (tingnan ang Fig. 2b sa Locality and error mitigation of quantum circuits).
Ang shaded lightcones (SLC) ay lumalampas pa, gumagamit ng classical simulations upang mas mahigpit na limitahan ang sensitivity sa mga error sa buong circuit. Ipinagpapalit nito ang ilang QPU time para sa CPU time at binabawasan ang sampling overhead na kailangan upang i-renormalize ang bias. Sa halip na isang hard cutoff, bawat potensyal na error sa circuit ay binibigyan ng isang graded na "shade" na nagbibigay-itaas-na-hangganan sa susceptibility ng expectation value ng observable sa error na iyon. (Para sa backward-propagated bounds na ipinakilala sa ibaba, ang susceptibility na ito ay nakadepende rin sa paunang estado ng circuit — ang all-zero na estado sa karaniwang workflows, at sa tutorial na ito.) Ang pinong karakterisasyong ito ay nagbibigay-daan sa mas mahusay, targeted na aplikasyon ng PEC na may bawas na variance, habang binibigyan ang user ng kakayahang i-tune nang may kontrol ang bias sa pagtantya ng observable. Tingnan ang Lightcone shading for classically accelerated quantum error mitigation para sa karagdagang detalye.
Ang ating workflow para sa SLC addon ay gumagamit ng samplomatic library kasama ng mga class na QuantumProgram at Executor na idinagdag sa Qiskit Runtime sa qiskit-ibm-runtime v0.47.0, na nagpapahintulot sa mga user na magkaroon ng mas modular na kontrol ng mga execution settings para sa error suppression at mitigation habang pinapanatili ang dali ng paggamit. Magbasa pa sa gabay na directed execution model.
Panorama ng SLC error-mitigation workflow
Para sa pagmomodelo ng noise ng QPU, gumagamit tayo ng sparse Pauli-Lindblad noise model na may one- at two-qubit Pauli error rates, na lokal na nabuo sa bawat qubit at edge ng device. Sa pagpiling ito, ang SLC error-mitigation workflow na ipinakita sa tutorial na ito ay ang mga sumusunod:
a. CPU — Igarantiya ang hangganan ng epekto-bawat-error ng one- at two-qubit Pauli errors
-
Forward propagation (paghangganan ng epekto sa observable). Ipalaganap ang bawat error hanggang sa dulo ng circuit at kalkulahin ang commutator nito sa observable.
-
I-truncate ang operator terms sa panahon ng evolution upang mapanatiling tractable ang computation.
-
Higit pang paigtingin ang mga hangganang ito sa pamamagitan ng isang maluwag na back-propagation ng observable batay sa quantum speed limits.
-
-
Backward propagation (paghangganan ng epekto sa paunang estado). Ipalaganap ang bawat error pabalik sa simula ng circuit at kalkulahin ang commutator nito sa paunang estado.
b. QPU — Alamin ang noise rates. Gamitin ang NoiseLearnerV3 upang tantiyahin ang mga rate ng Pauli-Lindblad noise model.
c. CPU — Bigyang-priyoridad ang mitigation
-
I-update ang pinagsamang hangganan gamit ang natutunang noise rates. Pagsamahin ang forward at backward bounds na naunang kinalkula at i-update ang mga ito gamit ang natutunang noise rates.
-
Ranggo ang mga noise components na papagaanin gamit ang kinalkulang hangganan at natutunang rates. Bigyang-priyoridad ang bawat posibleng noise error batay sa tinatayang epekto nito sa bias at ang kaugnay na gastos upang itama ito.
d. QPU — Ikintal ang anti-noise at patakbuhin. Isagawa ang circuit na interesado gamit ang anti-noise (inverse noise) na tinutukoy sa pamamagitan ng Box annotations.
e. CPU — Tantiyahin ang observable. Kalkulahin ang expectation value, na naglalapat ng measurement-based post-selection upang bawasan ang epekto ng non-Markovian noise.
Pangkalahatang-ideya ng noise learning
Ang noise learning ay isang karaniwang hakbang sa ilang mga pamamaraan ng error-mitigation, na isinasagawa ng noise learner; lumilitaw rin ito sa probabilistic error amplification tutorial. Sa NoiseLearnerV3, maaari mong tukuyin nang partikular ang mga noise layer na pag-aaralan bilang mga CircuitInstruction object, upang makalkula mo ang ninanais na SLC noise bounds para sa bawat layer sa paraang inilarawan sa itaas. Ang natutunang Pauli-Lindblad model ay nagbibigay ng coefficients na gagamitin sa PEC+SLC prioritization. Ang paraan kung paano pinagsasama-sama ang mga gate sa mga layer ay maaaring matukoy gamit ang mga convenience function na generate_boxing_pass_manager at find_unique_box_instructions, at pagkatapos ay ipasok sa SLC utility function na generate_noise_model_paulis, tulad ng inilarawan sa Hakbang 2 sa ibaba.
| Bahagi 1 | Bahagi 2 | Bahagi 3 |
|---|---|---|
| I-Pauli-twirl ang two-qubit gate layers | Ulitin ang identity pairs ng mga layer at alamin ang noise | Kunin ang isang fidelity (error para sa bawat noise channel) |
![]() | ![]() |
Pangkalahatang-ideya ng post-processing
Pagkatapos mag-execute sa quantum hardware gamit ang samplomatic at Executor framework, ico-convert natin ang ating bitstring measurements sa gustong observable value. Sa tutorial na ito, ipapakita natin ang workflow sa isang mirrored Ising circuit (na binuo sa Step 1 sa ibaba), kung saan ang ideal expectation value ng minisurang observable ay 1 dahil ang second half ng circuit ay nag-a-undo sa first half. Kapag kino-compute ang observable value gamit ang executor_expectation_values function, naglalapat tayo ng ilang post-processing techniques na nagpapababa ng epekto ng noise. Kasama dito ang pag-alis ng shots na apektado ng non-Markovian noise, readout-error mitigation, at pagsasaalang-alang sa mga detalye ng ating PEC implementation. Tatalakayin ang mga detalye sa Step 4 sa ibaba.
Requirements
Bago simulan ang tutorial na ito, siguraduhing naka-install mo na ang mga sumusunod:
-
Qiskit SDK v2.2 o mas bago, na may visualization support
-
Qiskit Runtime v0.47 o mas bago (
pip install qiskit-ibm-runtime) -
Shaded lightcones Qiskit addon v0.1 o mas bago (
pip install qiskit-addon-slc) -
Qiskit addon utils v0.3 o mas bago (
pip install qiskit-addon-utils) -
Samplomatic v0.13 o mas bago (
pip install samplomatic)
Setup
Una, i-import ang mga package at function na kailangan para patakbuhin ang notebook na ito.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-slc qiskit-addon-utils qiskit-ibm-runtime samplomatic
from multiprocessing import set_start_method
# Setting this value prevents itertools.starmap deadlock on UNIX systems
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 numpy as np
from matplotlib import pyplot as plt
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager, PassManager
from qiskit_ibm_runtime import (
QiskitRuntimeService,
QuantumProgram,
Executor,
NoiseLearnerV3,
)
import samplomatic
from samplomatic.utils import find_unique_box_instructions
from samplomatic.transpiler import generate_boxing_pass_manager
from qiskit_addon_utils.exp_vals.measurement_bases import (
get_measurement_bases,
)
from qiskit_addon_utils.exp_vals.expectation_values import (
executor_expectation_values,
)
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_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
Halimbawa ng maliitang simulator
Tulad ng iba pang learning-based error mitigation methods, ang PEC na may shaded lightcones ay nagpapagaan sa physical na noise ng isang specific quantum processor, kaya umaasa ito sa hardware services na walang makabuluhang analog sa isang ideal simulator:
-
Ang
NoiseLearnerV3ay experimental na nagkaka-characterize ng sparse Pauli-Lindblad noise channel sa bawat unique two-qubit layer. Sa isang noiseless simulator, walang noise na kakailanganing kanselahin. -
Ang
Executorprimitive ay nagsa-sample ng twirled, anti-noise-injected circuits na ginenerate ngsamplomaticsa isang backend.
Ang shaded-lightcone bound computation ay classical, ngunit makabuluhan lamang ito kaugnay ng learned hardware noise rates, na siyang nagtatakda ng mitigation budget at sampling overhead. Dahil dito, nilalaktawan natin ang small-scale simulator example at direktang ipinapakita ang buong PEC+SLC workflow sa hardware, na may bawat step ng Qiskit pattern na binreak down sa ibaba.
Halimbawa ng malakihang hardware
Pinapatakbo natin ang kumpletong PEC+SLC workflow sa isang 20-qubit mirrored Ising circuit na na-execute sa IBM Quantum® hardware, sinusunod ang apat na step ng isang Qiskit pattern.
Hakbang 1: I-map ang problema
Ganito ang setup ng ating demonstration:
-
Pinipili nating magsagawa ng time evolution sa ilalim ng isang 1D Ising chain Hamiltonian. Ang 1D Ising chain ay nagbibigay ng magandang siksik na circuit structure, na convenient para sa pagpapakita ng mga PEC implementation.
-
Pinipili nating sukatin ang observable na , na naglalaman ng isang at isang component. Convenient ito para maipakita kung paano magkaiba ang pagtrato ng SLC bounds sa iba't ibang uri ng errors.
-
Gusto natin na ang ideal expectation value ng observable na ito ay eksaktong 1, para madaling husgahan ang kalidad ng mitigated estimates. Ang pag-mirror sa circuit ay ang karamihan ng solusyon dito: para sa bawat gate sa second half ng circuit, may inverse gate sa first half, kaya ang ideal circuit ay kumikilos bilang identity.
-
Sa huli, ang pagkakaroon ng ideal expectation value na 1 ay nangangailangan na ang circuit ay magsimula — at dahil dito, dahil sa mirroring, ideal din na magtapos — sa isang +1 eigenstate ng observable. Dahil ang observable ay naglalaman ng non- component, at ang
Executorprimitive ay isinasaalang-alang ang gustong measurement basis sa dulo ng circuit, idinagdag natin ang kaukulang basis-preparation gates sa simula ng mirror circuit gamit angprepare_basisfunction. Ginagamit natin angget_measurement_basesfunction para matukoy kung anong mga gates ang kailangan at kung saan ilalagay ang mga ito, habang sinusubaybayan ang mga subtlety sa qubit-index na nagmumula saboxannotation conventions, tulad ng tinalakay sa seksyon tungkol sa paghahanda ng canonical basis measurements.
# Width of the Ising chain (a free parameter of this demonstration)
num_qubits = 20
# The observable to measure: one X component (on qubit 6) and one Z component
# (on qubit 13), as motivated in "Step 1: Map the problem" above
target_obs_sparse = [("XZ", [6, 13], 1.0)]
observable = SparsePauliOp.from_sparse_list(
target_obs_sparse, num_qubits=num_qubits
)
# Determine the measurement basis each qubit requires for this observable.
# The reverser is used during post-processing in Step 4.
bases_virt, reverser_virt = get_measurement_bases(observable)
# Free parameters of the demonstration circuit: the number of Trotter steps
# sets the circuit depth, and rx_angle is the transverse-field rotation angle
# applied in each step (pi/4 makes the dynamics non-Clifford)
num_trotter_steps = 10
rx_angle = np.pi / 4
def construct_ising_circuit(
num_qubits: int,
num_trotter_steps: int,
rx_angle: float,
) -> QuantumCircuit:
circuit = QuantumCircuit(num_qubits)
for _ in range(num_trotter_steps):
circuit.rx(rx_angle, range(num_qubits))
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)
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 == 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) -> QuantumCircuit:
mirror_circ = circuit.copy_empty_like()
mirror_circ.compose(circuit.inverse(), inplace=True)
mirror_circ.barrier()
mirror_circ.compose(circuit, inplace=True)
mirror_circ.measure_active()
return mirror_circ
# Instantiate the mirrored circuit and prepend the basis-preparation gates
circuit = construct_ising_circuit(num_qubits, num_trotter_steps, rx_angle)
mirrored_circuit = mirror_circuit(circuit)
mirrored_circuit = prepare_basis(mirrored_circuit, bases_virt[0])
mirrored_circuit.draw(
"mpl", fold=-1, scale=0.3, idle_wires=False, measure_arrows=False
)

Step 2: Optimize
Ino-optimize natin ang mga detalyeng kaugnay ng circuit na tatakbuhin, ang observable na susukatin, at ang mga noise-learning parameters. Bilang panimulang punto, pinipili natin ang least-busy Heron processor at ino-instantiate ito na naka-on ang fractional gates. Ang mga fractional gates na ito ay nagbibigay ng mas malaking sensitivity sa ilan sa ating post-selection filtering.
# Initialize IBM Quantum Compute Service using your saved credentials
service = QiskitRuntimeService()
# Select the least busy Heron device. This tutorial targets Heron processors:
# the usage estimate and the non-Markovian-noise post-selection discussed in
# Step 4 are specific to this processor family.
backend = service.least_busy(
operational=True,
simulator=False,
filters=lambda b: b.processor_type["family"] == "Heron",
)
# Re-fetch with fractional gates enabled (least_busy does not forward this)
# Fractional gates are enabled so the non-Clifford Rx rotations are supported natively.
backend = service.backend(backend.name, use_fractional_gates=True)
print(f"Selected backend: {backend.name}")
Selected backend: ibm_kingston
Una, i-transpile natin ang ating circuit sa ISA instructions, na kailangan para sa execution sa ating mga QPU. Ang pagpili ng physical qubits na patatakbuhan ay may malaking epekto sa kalidad ng mga resulta, dahil ang coherence times at gate fidelities ay nag-iiba sa buong device at nagbabago sa paglipas ng panahon. Para sa isang maingat na eksperimento, maaaring makatulong ang manu-manong pagpili ng chain ng qubits base sa mabilis na benchmarking experiments (tingnan halimbawa, ang Real-time benchmarking for qubit selection tutorial). Para sa layunin ng tutorial na ito, umaasa tayo sa Qiskit transpiler para pumili ng layout base sa coupling map ng backend at sa pinakabagong calibration data nito.
isa_pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = isa_pm.run(mirrored_circuit)
# The chain of physical qubits selected by the transpiler
layout = isa_circuit.layout.final_index_layout()
print(f"Selected layout: {layout}")
isa_observable = observable.apply_layout(
layout, num_qubits=isa_circuit.num_qubits
)
Selected layout: [71, 58, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 36, 21, 22, 23, 16, 3, 2]
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,
)

I-box ang circuit
Para sa madaling implementation, ginagamit natin ang generate_boxing_pass_manager transpilation pass, na naglalagay ng circuit instructions sa mga annotated boxes. Malinaw na ipinapakita ng mga box na ito kung saan, sa kaso ng PEC, dapat i-inject ang anti-noise sa circuit. Para sa mga detalye tungkol sa settings, tingnan ang Samplomatic documentation.
Tandaan na ang SLC workflow ay nangangailangan ng paggamit ng inject_noise_strategy="individual_modification" sa susunod na bahagi ng proseso dahil pinapayagan nito tayong tukuyin nang unique ang mga InjectNoise annotations ng BoxOp instructions sa circuit. (Ang mga box na walang InjectNoise annotation ay hindi matutukoy sa ganitong paraan.)
Ang SLC bound computations ay kasalukuyang ipinapalagay na ang mga InjectNoise annotations ay inilalagay gamit ang site="after" (tingnan ang qiskit-addon-slc#33). Dahil nagbago ang default na halaga ng setting na ito sa mga bagong bersyon ng samplomatic (tingnan ang samplomatic#372), itinakda namin ang inject_noise_site="after" nang malinaw sa ibaba.
Ang find_unique_box_instructions function ay dumadaan sa binigay na boxed circuit at tinutukoy ang mga may unique two-qubit (2Q) layers o measurements, para sa layunin ng noise learning at 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_site="after",
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
)
boxed_circuit.draw(
"mpl",
fold=-1,
scale=0.3,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)

Ihanda ang mga sukat ng canonical bases
Tandaan na dapat tayong mag-ingat nang husto para masubaybayan ang qubit ordering. Sa ibaba, ipinapakilala natin ang konsepto ng canonical_qubits bilang paraan upang angkop na i-update ang qubit ordering kapag ito ay ibinibigay sa Executor, bunga ng kung paano nakukuha ang qubit order kapag nagbo-box ng circuits at naghahanap ng unique instructions. Tingnan ang Qubit ordering convention documentation para sa mga detalye.
# 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
]
Workflow para sa lightcone shading, noise learning, at anti-noise injection
Sa tutorial na ito, pinapatakbo natin ang SLC bound computations bago matapos ang noise learning, kaya ang circuit na kailangang i-mitigate ay pinapatakbo nang malapit hangga't maaari sa oras sa learned noise model. Sa prinsipyo, ang workflow na ito ay maaari pang mai-parallelize: maaaring tumakbo ang isang noise-learning job habang, kaparehong oras, tinatantya ang noise bounds. Para sa isang arbitrary na quantum circuit, ang noise-bound computation ay maaaring mag-scale nang may mahinang exponential dependence, kaya ang pag-parallelize sa bound computation (halimbawa, sa maraming CPU core) ay nagbubunga ng mas mahigpit na bounds para sa isang partikular na compute-time budget, at ang mga QPU execution at bound computations mismo ay maaaring i-parallelize para sa pinakaepektibong workflow.
Hulaan ang mga Pauli ng noise-model na matututunan
Bago magpatakbo ng kahit anong noise learning sa hardware, una nating ilalatag kung anong mga Pauli error terms ang mapapaloob sa noise model natin. Alalahanin na minomodelo natin ang noise ng bawat unique layer bilang isang sparse Pauli-Lindblad model na may isang error rate para sa bawat weight-one Pauli (, , o ) sa bawat active qubit, at isa para sa bawat weight-two Pauli sa bawat connected pair ng active qubits. Ang generate_noise_model_paulis function ay dumadaan sa bawat unique boxed layer ng binigay na circuit at ginegenerate mismo ang listahan ng mga Pauli terms na ito, isinasaalang-alang ang qubit connectivity ng circuit.
Sa madaling salita, hinuhulaan ng step na ito ang structure ng noise model na pupunan ng NoiseLearnerV3 sa susunod ng learned rates. Ang pagkaalam ng structure na ito nang maaga ang nagpapahintulot sa atin na kalkulahin ang forward at backward bound para sa bawat posibleng error bago pa man tumakbo ang noise learning.
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. Kalkulahin ang mga forward bound
Ang compute_forward_bounds function ay sinusuri ang commutation relations sa pagitan ng mga gates sa bawat layer at ang mga Pauli terms na nagenerate sa itaas batay sa kung paano nakaaapekto ang forward-propagation errors sa gustong observable . Para sa mga gates na commute sa Pauli terms, walang ginagawa. Para sa Clifford gates, itinutulak sila patungo sa simula ng circuit. Para sa non-Clifford gates, ina-approximate natin ang kanilang impluwensya sa target observables upang bigyan ng prayoridad sa susunod para sa noise cancellation (matapos i-merge ang lahat ng bounds). Ang bound na ito ay nakakamit sa pamamagitan ng unang paglapat ng L2 norm (ibig sabihin, ang square root ng sum of squares ng mga kaugnay na Pauli-term coefficients). Kapag masyadong marami ang qubit terms na kasangkot, bumabalik tayo sa mas maluwag na bound gamit ang triangle inequality.
Itakda ang mga parameter ng bound-computation
Ang mga sumusunod na settings ang kumokontrol sa classical bound computations:
-
slc_atol: ang absolute tolerance ng mga computation. Ang mga operator terms na may coefficients na mas mababa sa threshold na ito ay itinuturing na negligible at itinatapon. -
slc_eigval_max_qubits: ang maximum na bilang ng qubits kung saan pinag-eevaluate ang mas mahigpit na, eigenvalue-based (L2-norm) bound. Ang mga operator na sinusuportahan sa mas maraming qubits kaysa dito ay babalik sa mas maluwag na triangle-inequality bound na inilarawan sa itaas. -
slc_evolution_max_terms: ang maximum na bilang ng Pauli terms na pinapanatili habang nagpo-propagate ang mga operator sa buong circuit. Ang pagtatanggal ng pinakamaliit na terms ay nagpapanatiling tractable ang computation kapalit ng bahagyang mas maluwag na bounds. -
slc_num_processes: ang bilang ng CPU processes kung saan pinag-paparallel ang bound computation. -
slc_timeout: ang time budget (sa segundo) para sa bound computation. Ang mga layer na hindi maabot sa loob ng budget na ito ay bibigyan ng trivial bound na 2.
Sa tutorial na ito, ang timeout ay naabot sa layunin: sinasadya nating panatilihing maliit ang slc_timeout para mabilis tumakbo ang notebook, at para ipakita na ligtas ang pag-abot sa timeout. Ang mga computed shades ay upper bounds sa bawat yugto, kaya ang pag-abot sa timeout ay nagpapaluwag lamang (trivial) sa bounds para sa mga layer na hindi naabot; hindi kailanman nito ina-underestimate ang epekto ng isang error. Makikita ito sa mga visualization sa ibaba bilang pinakakaliwang mga layer na puno nang buo ng trivial bound. Ang pagtaas ng timeout (o ng bilang ng mga process) ay nagpapahigpit sa bounds at higit na nagpapababa sa sampling overhead.
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,
)
Bounds computation timed out.
I-visualize ang SLC para sa manual na inspeksyon
Maaari mong i-interpret ang behavior ng shaded bounds sa pamamagitan ng pagsusuri kung paano nag-i-interact ang measurements at Pauli terms sa local errors. Ang mga pattern na ito ay katangian ng kicked Ising Hamiltonian time-evolution problem na ito at lumalabas din sa papel na Lightcone Shading for Classically Accelerated Quantum Error Mitigation, na may ilang telltale features:
-
Malinaw nating natutukoy ang dalawang cone na nagmumula sa dalawang non-identity Paulis sa observable.
-
Makikita natin na ang X measurement sa qubit 6 ay commute sa X error sa pinakakanang layer.
-
Makikita natin na ang Z Pauli sa qubit 13 ay commute sa Z error sa pinakakanang layer.
-
Kapag naabot natin ang timeout na tinukoy sa itaas, ang mga natitirang layers sa kaliwa ay pupunan nang lubusan ng trivial bounds na dalawa.
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. Higpitan ang mga forward bound gamit ang quantum speed limit
Sa susunod, hinihigpitan natin ang bounds gamit ang tighten_with_speed_limit function. Ang geometric spread ng lightcone ng observable ay ganap nang naisaalang-alang sa forward bounds na nakalkula sa itaas. Ang idinaragdag ng function na ito ay isang self-consistency pass na kumukuha ng ideya mula sa limitadong bilis kung saan makakadaan ang impormasyon sa circuit — lumalaki ang lightcone ng hindi hihigit sa isang qubit kada layer ng two-qubit gates — upang higpitan ang dati nang nakalkulang bounds kaugnay ng isa't isa. Dumadaloy ang impormasyon sa dalawang direksyon:
-
Mula sa commutator bounds patungo sa bounds sa backpropagated observable: kung ang observable ay commute sa isang error sa isang qubit, hindi ito maaaring magkaroon ng malaking o component doon.
-
Mula sa bounds sa backpropagated observable patungo sa commutator bounds: kung ang observable ay may kaunting at component sa isang qubit, dapat itong halos commute sa doon.
forward_bounds_tighter = tighten_with_speed_limit(
forward_bounds, boxed_circuit, noise_model_paulis, isa_observable
)
I-visualize ang SLC para sa manual na inspeksyon
Sa prinsipyo, ang pagpapahigpit na ito ay nagbibigay ng mas maayos na transition sa pagitan ng nakalkulang bounds at ng trivial bounds na ibinigay sa mga layer lagpas sa timeout. Sa halimbawang ito, halos hindi makikita ang epekto, dahil ang mga lightcone ay kumalat na sa mga gilid ng circuit bago pa man maabot ang timeout.
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. Kalkulahin ang mga backward bound
Sinusuri ng bahaging ito ng noise prediction kung paano nakaaapekto ang isang error sa isang partikular na layer sa input state . Ang compute_backward_bounds function ay unang binabaligtad ang circuit at inaalis ang measurement gates, at pagkatapos ay nagpapatuloy sa katulad na pagsusuri gaya ng ginawa para sa forward-bound computations.
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,
)
I-visualize ang SLC para sa manual na inspeksyon
Mula sa pagkalkula ng backward bounds, makikita natin kung paano kinokontrol ng initial state structure ang maagang behavior ng error propagation:
-
Malinaw nating makikita kung paano ang mga Z errors ay unang commute sa |0⟩ initial state.
-
Sa qubit 6 lamang, kung saan ino-initialize natin ang +1 eigenstate ng X basis, hindi nagko-commute ang isang Z error, samantalang ang isang X error ay nagko-commute.
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,
)
)



I-preview ang mga pinagsamang bound nang walang natutunang noise rate
Tinutukoy ng merged_bounds function ang punto sa circuit kung saan ang paglipat mula sa backward bounds patungo sa forward bounds ay nagpapaliit sa kabuuang estimated bias sa gustong observable. Ang bias na ito ay kinakalkula bilang kabuuan ng backward-bound contributions para sa lahat ng noise locations bago ang puntong iyon, kasama ang forward-bound contributions para sa lahat ng noise locations pagkatapos nito. Sa kasalukuyan, ginagawa ito nang pantay para sa lahat ng qubits.
Ang optimal na punto kung saan lilipat mula sa backward patungo sa forward bounds ay nakadepende sa learned noise rates. Sa yugtong ito ay wala pa tayong natutunang rates: ang noise_model_rates ay in-initialize sa None para sa bawat layer, na tinuturing ng merge_bounds bilang uniform noise rates. Kaya, ang merged bounds na ipinapakita rito ay isang preview lamang; kinakalkula natin muli ang mga ito gamit ang aktwal na learned rates sa Step 3.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
noise_model_rates,
)
Missing noise rates. Partitioning backward/forward commutator bounds by assuming uniform error rates.
Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
I-visualize ang SLC para sa manual na inspeksyon
Pagkatapos i-merge ang backward at tightened forward bounds, malinaw na ang behavior ng pinagsamang mga SLC:
-
Sinasabi sa atin ng function sa itaas na may pinipiling partition kung saan naganap ang paglipat mula sa backward patungo sa tightened forward bounds.
-
Makikita natin sa ibaba na ang mga SLC ngayon ay naglalaman ng partial backward at partial tightened forward bounds.
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,
)
)



Hakbang 3: Isagawa
Sa seksyong ito, sisimulan natin ang bahagi ng workflow na gumagamit ng isang tunay na quantum device. Para sa learning-based error mitigation method na ito, mayroong dalawang step:
-
Alamin ang noise gamit ang
NoiseLearnerV3. -
Patakbuhin ang isang error-mitigation circuit gamit ang
samplomaticatExecutorframework.
Gamit ang bounded errors mula sa ating quantum circuit, natutunan natin ang kaukulang noise rates upang bigyan ng prayoridad ang ating error budget, tukuyin ang sampling overhead, at magpatakbo sa isang QPU.
a. Alamin ang mga noise rate
Ikina-characterize ng noise learner ang mga noise processes na nakaaapekto sa mga gates sa isa o higit pang circuits na pinag-aaralan, batay sa sparse Pauli-Lindblad noise model. Ang run() method ay naglulunsad ng noise-learning job para sa binigay na unique two-qubit layers, gamit ang mga options na tinukoy sa noise-learner configuration. Kinokontrol ng mga options na ito ang Pauli-twirling strategy, ang bilang ng randomizations at shots, ang learning depths, at post-selection.
Pinipili rin natin nang may sinasadya ang learning depths. Isang praktikal na natuklasan para sa learning-based mitigation gamit ang samplomatic ay na lubos na kapaki-pakinabang na ang pinakamalalim na learning depth ay tumutugma sa depth ng circuit na gusto mong i-mitigate. Dahil ang NLv3 layer_pair_depths ay sinusukat sa layer pairs (isang layer kasama ang kabaligtaran nito), itinatakda natin ang pinakamalalim na value sa kalahati ng two-qubit-layer depth ng circuit.
post_selection_enabled = True
# Match the deepest noise-learning depth to the depth of the circuit being
# mitigated. NLv3 ``layer_pair_depths`` are measured in layer pairs (a layer
# plus its inverse), so the deepest value is half the circuit's two-qubit-layer
# depth. Learning to this depth markedly improves the quality of the mitigation.
#
# We measure the two-qubit-layer depth on the pre-boxed ISA circuit: after
# boxing, every two-qubit gate is hidden inside a full-width ``BoxOp``, so a
# ``num_qubits == 2`` filter on ``boxed_circuit`` matches nothing (and
# ``QuantumCircuit.depth`` does not recurse into boxes).
depth_2q = isa_circuit.depth(lambda instr: instr.operation.num_qubits == 2)
max_layer_pair_depth = depth_2q // 2 # dividing by 2 since we want pairs
# Use a fixed schedule of learning depths, but drop any that exceed the circuit's
# depth and always cap the deepest value at ``max_layer_pair_depth`` so we never
# learn deeper than the circuit being mitigated.
candidate_depths = [1, 2, 4, 8, 12, 16, 24, 32, 40, 48]
layer_pair_depths = sorted(
{d for d in candidate_depths if d < max_layer_pair_depth}
| {max_layer_pair_depth}
)
noise_learner_options = {
"num_randomizations": 64,
"shots_per_randomization": 128,
"layer_pair_depths": layer_pair_depths,
"post_selection": {
"enable": post_selection_enabled,
"strategy": "edge",
"x_pulse_type": "rx",
},
"environment": {"job_tags": ["TUT_SLC"]},
}
noise_learner = NoiseLearnerV3(backend, noise_learner_options)
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.71
# Get a dict mapping each InjectNoise.ref to its learned PauliLindbladMap
refs_2_plm = noise_learner_result.to_dict(
unique_2q_instructions, require_refs=False
)
b.i. I-update ang mga pinagsamang bound gamit ang aktwal na natutunang noise rate
Ngayong natutunan na ang specific noise model, maaari na nating ilapat ang learned noise rates sa predicted noise bounds at makakuha ng huling pagpapasya kung aling bounds ang may pinakamalaking epekto sa pagpapaliit ng bias.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
refs_2_plm,
)
Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
b.ii. Compute the local_scales for the hardware execution
Sinusuri ng compute_local_scales ang bawat posibleng noise error sa circuit at tinatantiya kung gaano kalaki ang maaaring maidulot na bias sa panghuling measurement, gayundin kung gaano kamahal ang pagtatama nito. Pagkatapos, itinutugma nito ang mga error base sa kung gaano kabuti ang mitigation sa mga ito at pinipili ang subset na nagpapaliit ng bias hangga't maaari, habang nananatili sa loob ng pinapayagang sampling-cost budget (o nakakamit ang gustong accuracy). Ang resulta ay isang set ng scaling factors na nagpapahiwatig kung aling mga error ang aktibong mi-mitigate-han at kung alin ang mananatiling hindi mi-mitigate-han (local_scales), kasama ang predicted total sampling cost overhead (sampling_costs) at natitirang bias (residual_bias_bound).
Ang kakayahang kontrolin ang gustong natitirang bias ay isang mahalagang feature ng SLC implementation ng PEC. Samantalang sa orihinal na implementation, palaging zero bias ang target ng sampling overhead, maaari nating i-tune ang kinakailangang sampling overhead na may trade-off sa expected na natitirang bias. Nakakatulong ito upang mapanatili ng user ang isang fixed sampling budget, na maaaring maging lalong kapaki-pakinabang kapag unang gumagawa ng prototype ng isang 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=12.750876317157042, sampling cost (gamma^2) = 162.58484685543633
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_)
Timbangin ang sampling overhead laban sa residual bias
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="PEC+SLC",
)
ax.set_yscale("log")
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,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=chosen_bias_thres,
)
print(
f"PEC+SLC sampling cost (gamma^2) = {sampling_cost} "
f"w/ remaining bias = {100 * residual_bias_bound:.1f}%"
)
PEC+SLC sampling cost (gamma^2) = 48.13646509622307 w/ remaining bias = 10.0%
c. Isagawa ang circuit of interest gamit ang antinoise
c.i. Ihanda ang template circuit gamit ang samplex
Ang samplex ay isang output ng build method ng Samplomatic, na nagko-encode ng lahat ng impormasyong kailangan para makagenerate ng randomized parameters para sa template_circuit. Ang mga ito ay ginagamit pagkatapos upang i-setup ang mga QuantumProgram objects, na pinapatakbo naman sa isang QPU gamit ang Executor primitive. Ang bawat QuantumProgram ay maaaring maglaman ng ilang items, na maaari mong isipin bilang isang pares ng template at samplex.
Tingnan ang Hello samplomatic tutorial para sa mga detalye.
# 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
c.ii. I-set up ang 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.keys()
}
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_samplex_item(
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.keys()
}
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_samplex_item(
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.keys()
}
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_samplex_item(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
c.iii. Isagawa ang program gamit ang Executor primitive
executor = Executor(backend)
job_exec = executor.run(program)
results_exec = job_exec.result()
Hakbang 4: I-post-process
Habang kinakalkula natin ang huling expectation value na gusto natin gamit ang executor_expectation_values, nag-iimplementa tayo ng ilang post-processing techniques upang matulungan tayong makakuha ng pinakamataas na kalidad ng resulta na posible. Una, inilalapat natin ang ating twirled readout error extinction (TREX), na sumasaklaw sa anumang mga error na nangyayari habang readout process. Pagkatapos, itinatama natin ang mga error dulot ng non-Markovian noise sa ating mga Heron backend gamit ang isang post-selection method. Sinusukat ng method na ito ang active at spectator qubits, pagkatapos ay naglalapat ng mabagal na rotation sa bawat qubit, at sinusukat muli. Sa mga pagkakataong hindi kinukumpirma ng dalawang measurement ang isang flipped qubit gaya ng inaasahan, itinatapon ang mga shot na ito sa pamamagitan ng paglalapat ng mask mula sa PostSelector. Sa loob ng mask computation, maaaring itakda ang isang specific na strategy upang mag-filter batay sa single-qubit nodes o neighboring spectator edges, na maaaring makaapekto sa parehong bilang ng shots na naifi-filter out at sa kalidad ng resulta.
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 post-selection
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 (24.1% of shots kept):
With node-based post selection (24.2% of shots kept):
Mula sa pagsusuri ng experimental results, direkta nating maikukumpara ang behavior ng iba't ibang approach: PEC, PEC na pinagsama sa SLC, at ang unmitigated baseline. Ilang specific na detalye na dapat itampok:
-
Ang unmitigated result ay nasa malayo sa 10% bias band (malapit sa 0.35) at hindi apektado ng bilang ng randomizations.
-
Sa device na ito, ang full PEC ay may sampling overhead na . Pagkatapos ng malalaking maagang pagbabago, nababawi ng plain-PEC estimate ang signal at nananatili sa paligid ng 1.1, bahagyang lumalampas sa eksaktong value. Tandaan na ang 10% bias band ay para lamang sa PEC+SLC estimator: ang plain PEC ay sa prinsipyo ay unbiased, kaya ang natitirang deviation na ito ay hindi saklaw ng isang bias guarantee kundi nagpapakita ng statistical fluctuations at mga kakulangan sa learned noise model, na makatwiran sa laki ng problemang ito.
-
Pinapaliit ng SLC ang overhead ng karagdagang ~3.4 beses (sa , para sa residual-bias bound na humigit-kumulang 10%). Ang PEC+SLC ay lumulukso rin nang malaki sa kaunting randomizations, unang umaabot pa nga sa unphysical region, ngunit ang estimate nito ay unti-unting nagko-converge patungo sa eksaktong value, nananatili malapit sa 1.0 nang maayos sa loob ng band. Tinutumbasan o hinihigitan nito ang accuracy ng plain PEC sa halos ikatlo ng sampling cost, na nagpapakita ng benepisyo ng lightcone shading.
-
Ang error bars ng parehong mitigated estimates ay lumiliit habang naipon ang randomizations; sa fixed na bilang ng randomizations, ang PEC+SLC estimator ay may mas maliit na statistical variance, na naaayon sa mas maliit nitong .
Tandaan na ang learned noise rates (at kaya rin ang overheads at mitigated estimates) ay nakadepende sa backend at sa calibration nito sa oras ng execution, kaya ang muling pagpapatakbo ng notebook na ito ay maaaring makagawa ng mga quantitatively na ibang halaga.
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)

Mga susunod na hakbang
Kung interesante sa iyo ang gawaing ito, maaaring maging interesado ka rin sa mga sumusunod na materyal:

