Pagsamahin ang mga opsyon sa error mitigation gamit ang Estimator primitive
Tantiya sa paggamit: 7 minuto sa isang Heron r2 processor (TANDAAN: Ito ay isang tantiya lamang. Ang iyong runtime ay maaaring mag-iba.)
Mga Resulta ng Pag-aaral
Iminumungkahi namin na maging pamilyar ang mga gumagamit sa mga sumusunod na paksa bago dumaan sa tutorial na ito:
- Ang mga pangunahing kaalaman sa dynamical decoupling, measurement error mitigation, gate twirling, at zero-noise extrapolation, gaya ng inilarawan sa gabay na ito.
Mga Kinakailangan
Pagkatapos dumaan sa tutorial na ito, dapat maunawaan ng mga gumagamit ang:
- Kung paano selective na ipinapatupad ang mga nabanggit na error mitigation techniques sa hardware.
- Kung paano sila naghahambing sa kanilang kakayahang i-mitigate ang hardware noise.
Panimula
Ang tutorial na ito ay nagsusuri sa mga opsyon sa error suppression at error mitigation na magagamit sa Estimator primitive mula sa Qiskit Runtime. Ipinapakita ng tutorial na ito kung paano ipatupad ang bawat isa sa mga sumusunod na paraan nang isa-isa:
- Dynamical decoupling
- Measurement error mitigation
- Gate twirling
- Zero-noise extrapolation (ZNE)
Tandaan na ang isang alternatibo sa pagpapatupad ng mga teknikang ito nang isa-isa ay ang pagpapatupad ng mga ito gamit ang isang resilience level, kung saan ang resilience_level ay kumukuha ng mga halaga na 0, 1, 2:
- 0 : Walang ipinapatupad na mitigation.
- 1 : Ipinapatupad ang measurement error mitigation.
- 2 : Ipinapatupad ang gate twirling, measurement error mitigation, at ZNE.
Sa tutorial na ito, bubuo kayo ng circuit at observable at magsusumite ng mga job gamit ang Estimator primitive na gumagamit ng iba't ibang kombinasyon ng mga setting sa error mitigation. Pagkatapos, iguguhit ninyo ang mga resulta upang obserbahan ang mga epekto ng iba't ibang setting. Karamihan sa tutorial ay gumagamit ng 10-qubit circuit upang gawing mas madali ang mga visualization, at sa dulo, palalakin ninyo ang workflow hanggang 50 qubits.
Mga Kinakailangan
Bago magsimula sa walkthrough na ito, siguraduhing mayroon kayong naka-install na mga sumusunod:
- Qiskit SDK v2.1 o mas bago, na may suportang visualization
- Qiskit Runtime v0.40 o mas bago (
pip install qiskit-ibm-runtime)
Paghahanda
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt
import numpy as np
from qiskit.circuit.library import efficient_su2, unitary_overlap
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Batch, EstimatorV2 as Estimator
Maliit na halimbawa ng simulator
Lilaktawan namin ang hakbang na ito dahil ang runtime error mitigation ay hindi sinusuportahan sa mga simulator.
Halimbawa sa hardware
Hakbang 1: I-map ang mga classical inputs sa isang quantum problem
Ang walkthrough na ito ay umaasa na ang classical problem ay nai-map na sa quantum. Magsimula sa pamamagitan ng pagbuo ng circuit at observable na susukathin. Bagaman ang mga teknikang ginagamit dito ay naaangkop sa maraming uri ng circuits, para sa kaginhawahan, ang walkthrough na ito ay gumagamit ng efficient_su2 circuit na kasama sa Qiskit circuit library.
Ang efficient_su2 ay isang parameterized quantum circuit na dinisenyo upang maging epektibong maisasagawa sa quantum hardware na may limitadong qubit connectivity, habang sapat pa ring maka-ekspresibo upang lutasin ang mga problema sa mga application domain tulad ng optimization at chemistry. Ito ay binuo sa pamamagitan ng pagsasalit-salit ng mga layer ng parameterized single-qubit gates na may layer na naglalaman ng fixed pattern ng two-qubit gates, para sa piniling bilang ng mga repetisyon. Ang pattern ng two-qubit gates ay maaaring tukuyin ng user. Dito ay maaari ninyong gamitin ang built-in na pairwise pattern dahil binabawasan nito ang circuit depth sa pamamagitan ng pag-pack ng two-qubit gates nang siksik hangga't maaari. Ang pattern na ito ay maaaring isagawa gamit lamang ang linear qubit connectivity.
n_qubits = 10
reps = 1
circuit = efficient_su2(n_qubits, entanglement="pairwise", reps=reps)
circuit.decompose().draw("mpl", scale=0.7)

Bilang ating observable, kunin natin ang Pauli operator na kumikilos sa huling qubit, . Tandaan na ang katotohanang ang huling qubit ay tumutugma sa unang elemento ng string na ito ay dahil sa paggamit ng Qiskit ng little-endian notation.
# Z on the last qubit (index -1) with coefficient 1.0
observable = SparsePauliOp.from_sparse_list(
[("Z", [-1], 1.0)], num_qubits=n_qubits
)
Sa puntong ito, maaari kayong magpatuloy upang patakbuhin ang inyong circuit at sukatin ang observable. Gayunpaman, nais din ninyong ikumpara ang output ng quantum device sa tamang sagot — ibig sabihin, ang teoretikal na halaga ng observable, kung ang circuit ay naisagawa nang walang error. Para sa maliliit na quantum circuits, maaari ninyong kalkulahin ang halagang ito sa pamamagitan ng pagsisimula ng circuit sa isang classical computer, ngunit hindi ito posible para sa mas malalaking, utility-scale na circuits. Maaari ninyong malutas ang isyung ito sa pamamagitan ng "mirror circuit" technique (kilala rin bilang "compute-uncompute"), na kapaki-pakinabang para sa pag-benchmark ng performance ng mga quantum device.
Mirror circuit
Sa mirror circuit technique, pinagsasama ninyo ang circuit sa inverse circuit nito, na nabuo sa pamamagitan ng pag-invert ng bawat gate ng circuit sa reverse order. Ang resultang circuit ay nagsasakatuparan ng identity operator, na maaaring trivial na i-simulate. Dahil ang istraktura ng orihinal na circuit ay napapanatili sa mirror circuit, ang pagsasagawa ng mirror circuit ay nagbibigay pa rin ng ideya kung paano gagana ang quantum device sa orihinal na circuit.
Ang sumusunod na code cell ay nag-aatas ng random parameters sa inyong circuit, at pagkatapos ay bumubuo ng mirror circuit gamit ang unitary_overlap class. Bago i-mirror ang circuit, idagdag ang isang barrier instruction dito upang pigilan ang transpiler mula sa pagsasama ng dalawang bahagi ng circuit sa magkabilang gilid ng barrier at magreresulta sa isang transpiled circuit na walang anumang gates.
# Generate random parameters
rng = np.random.default_rng(1234)
params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)
# Assign the parameters to the circuit
assigned_circuit = circuit.assign_parameters(params)
# Add a barrier to prevent circuit optimization of mirrored operators
assigned_circuit.barrier()
# Construct mirror circuit
mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)
mirror_circuit.decompose().draw("mpl", scale=0.7)

Hakbang 2: I-optimize ang problema para sa quantum hardware execution
Kailangan ninyong i-optimize ang inyong circuit bago ito patakbuhin sa hardware. Ang prosesong ito ay nagsasangkot ng ilang hakbang:
- Pumili ng qubit layout na nag-map ng mga virtual qubits ng inyong circuit sa physical qubits sa hardware.
- Mag-insert ng swap gates kung kinakailangan upang i-route ang mga interaction sa pagitan ng mga qubits na hindi konektado.
- I-translate ang mga gates sa inyong circuit sa Instruction Set Architecture (ISA) instructions na maaaring direktang maisagawa sa hardware.
- Magsagawa ng circuit optimizations upang mabawasan ang circuit depth at gate count.
Ang transpiler na built-in sa Qiskit ay maaaring magsagawa ng lahat ng mga hakbang na ito para sa inyo. Dahil ang halimbawang ito ay gumagamit ng hardware-efficient circuit, ang transpiler ay dapat na makapili ng qubit layout na hindi nangangailangan ng anumang swap gates na iinsert para sa pag-route ng mga interaction.
Kailangan ninyong piliin ang hardware device na gagamitin bago ninyo i-optimize ang inyong circuit. Ang sumusunod na code cell ay humihiling ng pinaka-abalang device na may hindi bababa sa 127 qubits.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(backend)
<IBMBackend('ibm_fez')>
Maaari ninyong i-transpile ang inyong circuit sa inyong piniling backend sa pamamagitan ng paglikha ng pass manager at pagkatapos ay pagpapatakbo ng pass manager sa circuit. Ang isang madaling paraan upang lumikha ng pass manager ay ang paggamit ng generate_preset_pass_manager function. Tingnan ang Transpile with pass managers para sa mas detalyadong paliwanag ng pag-transpile gamit ang pass managers.
pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=1234
)
isa_circuit = pass_manager.run(mirror_circuit)
isa_circuit.draw("mpl", idle_wires=False, scale=0.7, fold=-1)

Ang transpiled circuit ay naglalaman na lamang ngayon ng ISA instructions. Lahat ng gates ay na-decompose na sa mga termino ng gates at rotations, at CZ gates.
Ang transpilation process ay nag-map ng mga virtual qubits ng circuit sa physical qubits sa hardware. Ang impormasyon tungkol sa qubit layout ay nakaimbak sa layout attribute ng transpiled circuit. Ang observable ay tinukoy din sa mga termino ng virtual qubits, kaya kailangan ninyong ilapat ang layout na ito sa observable, na maaari ninyong gawin sa apply_layout method ng SparsePauliOp.
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Original observable:")
print(observable)
print()
print("Observable with layout applied:")
print(isa_observable)
Original observable:
SparsePauliOp(['ZIIIIIIIII'],
coeffs=[1.+0.j])
Observable with layout applied:
SparsePauliOp(['IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Hakbang 3: Isagawa gamit ang Qiskit primitives
Handa na kayong patakbuhin ang inyong circuit gamit ang Estimator primitive.
Dito ay magsusumite kayo ng limang hiwalay na jobs, simula sa walang error suppression o mitigation, at sunud-sunod na paganahin ang iba't ibang opsyon sa error suppression at mitigation na available sa Qiskit Runtime. Para sa impormasyon tungkol sa mga opsyon, tingnan ang mga sumusunod na pahina:
- Overview of all options
- Dynamical decoupling
- Resilience, including measurement error mitigation and zero-noise extrapolation (ZNE)
- Twirling
Dahil ang mga job na ito ay maaaring tumakbo nang nakapag-iisa sa isa't isa, maaari ninyong gamitin ang batch mode upang pahintulutan ang Qiskit Runtime na i-optimize ang timing ng kanilang pagsasagawa.
pub = (isa_circuit, isa_observable)
jobs = []
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.environment.job_tags = [
"TUT_CEM_SS"
] # add tag for this small scale job
# Set number of shots
estimator.options.default_shots = 100_000
# Disable runtime compilation and error mitigation
estimator.options.resilience_level = 0
# Run job with no error mitigation
job0 = estimator.run([pub])
jobs.append(job0)
# Add dynamical decoupling (DD)
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XpXm"
job1 = estimator.run([pub])
jobs.append(job1)
# Add readout error mitigation (DD + TREX)
estimator.options.resilience.measure_mitigation = True
job2 = estimator.run([pub])
jobs.append(job2)
# Add gate twirling (DD + TREX + Gate Twirling)
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
job3 = estimator.run([pub])
jobs.append(job3)
# Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
job4 = estimator.run([pub])
jobs.append(job4)
Hakbang 4: I-post-process at ibalik ang resulta sa nais na classical format
Sa wakas, maaari ninyong suriin ang data. Dito ay kukuhanin ninyo ang mga job results, kukunin ang mga nasukat na expectation values mula sa kanila, at iguguhit ang mga values, kasama ang mga error bars ng isang standard deviation.
# Retrieve the job results
results = [job.result() for job in jobs]
# Unpack the PUB results (there's only one PUB result in each job result)
pub_results = [result[0] for result in results]
# Unpack the expectation values and standard errors
expectation_vals = np.array(
[float(pub_result.data.evs) for pub_result in pub_results]
)
standard_errors = np.array(
[float(pub_result.data.stds) for pub_result in pub_results]
)
# Plot the expectation values
fig, ax = plt.subplots()
labels = ["No mitigation", "+ DD", "+ TREX", "+ Twirling", "+ ZNE"]
ax.bar(
range(len(labels)),
expectation_vals,
yerr=standard_errors,
label="experiment",
)
ax.axhline(y=1.0, color="gray", linestyle="--", label="ideal")
ax.set_xticks(range(len(labels)))
ax.set_xticklabels(labels)
ax.set_ylabel("Expectation value")
ax.legend(loc="upper left")
plt.show()
Sa maliit na sukat na ito, mahirap makita ang epekto ng karamihan sa mga error mitigation techniques, ngunit ang zero-noise extrapolation ay nagbibigay ng makabuluhang pagpapabuti. Gayunpaman, tandaan na ang pagpapabuting ito ay hindi libre, dahil ang ZNE result ay mayroon ding mas malaking error bar.
Malaking halimbawa sa hardware
Kapag bumubuo ng eksperimento, kapaki-pakinabang na magsimula sa isang maliit na circuit upang gawing mas madali ang mga visualization at simulations. Ngayong nabuo at nasubukan ninyo ang ating workflow sa isang 10-qubit circuit, maaari na ninyong palakihin ito hanggang 50 qubits. Ang sumusunod na code cell ay inuulit ang lahat ng mga hakbang sa walkthrough na ito, ngunit ngayon ay inilalapat ang mga ito sa isang 50-qubit circuit.
n_qubits = 50
reps = 1
# Construct circuit and observable
circuit = efficient_su2(n_qubits, entanglement="pairwise", reps=reps)
observable = SparsePauliOp.from_sparse_list(
[("Z", [-1], 1.0)], num_qubits=n_qubits
)
# Assign parameters to circuit
params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)
assigned_circuit = circuit.assign_parameters(params)
assigned_circuit.barrier()
# Construct mirror circuit
mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)
# Transpile circuit and observable
isa_circuit = pass_manager.run(mirror_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
# Run jobs
pub = (isa_circuit, isa_observable)
jobs = []
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.environment.job_tags = [
"TUT_CEM_LS"
] # add tag for this large scale job
# Set number of shots
estimator.options.default_shots = 100_000
# Disable runtime compilation and error mitigation
estimator.options.resilience_level = 0
# Run job with no error mitigation
job0 = estimator.run([pub])
jobs.append(job0)
# Add dynamical decoupling (DD)
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XpXm"
job1 = estimator.run([pub])
jobs.append(job1)
# Add readout error mitigation (DD + TREX)
estimator.options.resilience.measure_mitigation = True
job2 = estimator.run([pub])
jobs.append(job2)
# Add gate twirling (DD + TREX + Gate Twirling)
estimator.options.twirling.enable_gates = True
estimator.options.twirling.num_randomizations = "auto"
job3 = estimator.run([pub])
jobs.append(job3)
# Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
job4 = estimator.run([pub])
jobs.append(job4)
# Retrieve the job results
results = [job.result() for job in jobs]
# Unpack the PUB results (there's only one PUB result in each job result)
pub_results = [result[0] for result in results]
# Unpack the expectation values and standard errors
expectation_vals = np.array(
[float(pub_result.data.evs) for pub_result in pub_results]
)
standard_errors = np.array(
[float(pub_result.data.stds) for pub_result in pub_results]
)
# Plot the expectation values
fig, ax = plt.subplots()
labels = ["No mitigation", "+ DD", "+ TREX", "+ Twirling", "+ ZNE"]
ax.bar(
range(len(labels)),
expectation_vals,
yerr=standard_errors,
label="experiment",
)
ax.axhline(y=1.0, color="gray", linestyle="--", label="ideal")
ax.set_xticks(range(len(labels)))
ax.set_xticklabels(labels)
ax.set_ylabel("Expectation value")
ax.legend(loc="upper left")
plt.show()
Kapag inihambing ninyo ang mga resulta ng 50-qubit sa mga resulta ng 10-qubit kanina, maaaring mapansin ninyo ang mga sumusunod (ang inyong mga resulta ay maaaring mag-iba sa iba't ibang runs):
- Lahat ng mga eksperimento ay nagbibigay ng mga resulta na mas malapit sa ideal na halaga at lahat ng mga error bar ay mas maliit.
- Ang pagdagdag ng dynamical decoupling ay maaaring nagpalala ng performance kumpara sa kaso na walang mitigation. Hindi ito nakakagulat, dahil ang circuit ay napakasikip. Ang dynamical decoupling ay pangunahing kapaki-pakinabang kapag may malalaking gaps sa circuit, kung saan ang mga qubits ay nakaupo nang idle nang walang mga gates na inilalapat sa kanila. Kapag ang mga gaps na ito ay wala, ang dynamical decoupling ay hindi epektibo, at maaari talagang magpalala ng performance dahil sa mga error sa mga dynamical decoupling pulses mismo. Ang 10-qubit circuit ay maaaring masyadong maliit para sa atin upang obserbahan ang epektong ito.
- Sa zero-noise extrapolation, ang resulta ay napakalapit sa ideal na halaga. Ipinakikita nito ang kapangyarihan ng ZNE.
Mga Susunod na Hakbang
Kung natagpuan ninyong kawili-wili ang gawaing ito, maaaring magustuhan ninyo ang sumusunod na materyal tungkol sa ilang karagdagang error mitigation at error suppression techniques na hindi nabanggit sa tutorial na ito: