Real-time benchmarking para sa pagpili ng qubit
Tantya sa paggamit: 4 na minuto sa isang Eagle r2 processor (PAALALA: Ito ay isang tantya lamang. Maaaring mag-iba ang iyong runtime.)
# Added by doQumentation β required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-experiments qiskit-ibm-runtime rustworkx
# This cell is hidden from users β it disables some lint rules
# ruff: noqa: E722
Backgroundβ
Ipinapakita ng tutorial na ito kung paano magsagawa ng real-time na mga eksperimento sa characterization at i-update ang mga katangian ng backend upang mapabuti ang pagpili ng qubit kapag nag-map ng circuit sa mga pisikal na qubit sa isang QPU. Matututunan ninyo ang mga pangunahing eksperimento sa characterization na ginagamit upang matukoy ang mga katangian ng QPU, kung paano gawin ang mga ito sa Qiskit, at kung paano i-update ang mga katangiang naka-save sa backend object na kumakatawan sa QPU batay sa mga eksperimentong ito.
Ang mga katangiang inuulat ng QPU ay ina-update nang isang beses sa isang araw, ngunit ang sistema ay maaaring mag-drift nang mas mabilis kaysa sa oras sa pagitan ng mga update. Maaari nitong maapektuhan ang pagiging maaasahan ng mga routine sa pagpili ng qubit sa Layout stage ng pass manager, dahil gagamitin nila ang mga inuulat na katangiang hindi kumakatawan sa kasalukuyang estado ng QPU. Dahil dito, maaaring sulit na maglaan ng ilang oras ng QPU para sa mga eksperimento sa characterization, na maaaring gamitin upang i-update ang mga katangian ng QPU na ginagamit ng Layout routine.
Mga Pangangailanganβ
Bago magsimula sa tutorial na ito, siguraduhin na mayroon kayo ng sumusunod na naka-install:
- Qiskit SDK v2.0 o mas bago, na may suporta sa visualization
- Qiskit Runtime v0.40 o mas bago (
pip install qiskit-ibm-runtime) - Qiskit Experiments v0.12 o mas bago (
pip install qiskit-experiments) - Rustworkx graph library (
pip install rustworkx)
Setupβ
from qiskit_ibm_runtime import SamplerV2
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import hellinger_fidelity
from qiskit.transpiler import InstructionProperties
from qiskit_experiments.library import (
T1,
T2Hahn,
LocalReadoutError,
StandardRB,
)
from qiskit_experiments.framework import BatchExperiment, ParallelExperiment
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session
from datetime import datetime
from collections import defaultdict
import numpy as np
import rustworkx
import matplotlib.pyplot as plt
import copy
Hakbang 1: I-map ang mga classical input sa isang quantum problemβ
Upang sukatin ang pagkakaiba sa performance, isinasaalang-alang natin ang isang circuit na naghahanda ng Bell state sa isang linear chain na may iba't ibang haba. Sinusukat ang fidelity ng Bell state sa mga dulo ng chain.
from qiskit import QuantumCircuit
ideal_dist = {"00": 0.5, "11": 0.5}
num_qubits_list = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 127]
circuits = []
for num_qubits in num_qubits_list:
circuit = QuantumCircuit(num_qubits, 2)
circuit.h(0)
for i in range(num_qubits - 1):
circuit.cx(i, i + 1)
circuit.barrier()
circuit.measure(0, 0)
circuit.measure(num_qubits - 1, 1)
circuits.append(circuit)
circuits[-1].draw(output="mpl", style="clifford", fold=-1)


I-set up ang backend at coupling mapβ
Una, pumili ng backend
# To run on hardware, select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
qubits = list(range(backend.num_qubits))
Pagkatapos ay kunin ang coupling map nito
coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
# Get unidirectional coupling map
one_dir_coupling_map = coupling_graph.edge_list()
Upang ma-benchmark ang pinakamaraming two-qubit gate nang sabay-sabay, hinihiwalay natin ang coupling map sa isang layered_coupling_map. Ang object na ito ay naglalaman ng listahan ng mga layer kung saan ang bawat layer ay isang listahan ng mga edge kung saan ang mga two-qubit gate ay maaaring isagawa nang sabay-sabay. Ito ay tinatawag ding edge coloring ng coupling map.
# Get layered coupling map
edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)
layered_coupling_map = defaultdict(list)
for edge_idx, color in edge_coloring.items():
layered_coupling_map[color].append(
coupling_graph.get_edge_endpoints_by_index(edge_idx)
)
layered_coupling_map = [
sorted(layered_coupling_map[i])
for i in sorted(layered_coupling_map.keys())
]
Mga eksperimento sa characterizationβ
Ang isang serye ng mga eksperimento ay ginagamit upang i-characterize ang mga pangunahing katangian ng mga qubit sa isang QPU. Ang mga ito ay , , readout error, at single-qubit at two-qubit gate error. Maikling bubuuin natin kung ano ang mga katangiang ito at tutukuyin ang mga eksperimento sa qiskit-experiments package na ginagamit upang i-characterize ang mga ito.
T1β
Ang ay ang karakteristikong oras na kinakailangan para sa isang excited qubit na bumagsak sa ground state dahil sa mga proseso ng amplitude-damping decoherence. Sa isang experiment, sinusukat natin ang isang excited qubit pagkatapos ng delay. Kung mas malaki ang delay time, mas malamang na bumagsak ang qubit sa ground state. Ang layunin ng eksperimento ay i-characterize ang decay rate ng qubit patungo sa ground state.
T2β
Ang ay kumakatawan sa dami ng oras na kinakailangan para sa Bloch vector projection ng isang qubit sa XY plane na bumagsak sa humigit-kumulang 37% () ng initial amplitude nito dahil sa mga proseso ng dephasing decoherence. Sa isang Hahn Echo experiment, matantya natin ang rate ng decay na ito.
Characterization ng state preparation at measurement (SPAM) errorβ
Sa isang eksperimento sa SPAM-error characterization, ang mga qubit ay inihahanda sa isang partikular na estado ( o ) at sinusukat. Ang probability ng pagsukat ng estado na iba sa naihanda ay nagbibigay ng probability ng error.
Single-qubit at two-qubit randomized benchmarkingβ
Ang Randomized benchmarking (RB) ay isang popular na protocol para sa pag-characterize ng error rate ng mga quantum processor. Ang isang RB experiment ay binubuo ng pagbuo ng mga random na Clifford circuit sa mga ibinigay na qubit na ang unitary na kinakalkula ng mga circuit ay ang identity. Pagkatapos patakbuhin ang mga circuit, binibilang ang bilang ng mga shot na nagresulta sa error (ibig sabihin, output na iba sa ground state), at mula sa data na ito ay maaaring mahinuha ang mga tantya ng error para sa quantum device, sa pamamagitan ng pagkalkula ng Error Per Clifford.
# Create T1 experiments on all qubit in parallel
t1_exp = ParallelExperiment(
[
T1(
physical_qubits=[qubit],
delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],
)
for qubit in qubits
],
backend,
analysis=None,
)
# Create T2-Hahn experiments on all qubit in parallel
t2_exp = ParallelExperiment(
[
T2Hahn(
physical_qubits=[qubit],
delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],
)
for qubit in qubits
],
backend,
analysis=None,
)
# Create readout experiments on all qubit in parallel
readout_exp = LocalReadoutError(qubits)
# Create single-qubit RB experiments on all qubit in parallel
singleq_rb_exp = ParallelExperiment(
[
StandardRB(
physical_qubits=[qubit], lengths=[10, 100, 500], num_samples=10
)
for qubit in qubits
],
backend,
analysis=None,
)
# Create two-qubit RB experiments on the three layers of disjoint edges of the heavy-hex
twoq_rb_exp_batched = BatchExperiment(
[
ParallelExperiment(
[
StandardRB(
physical_qubits=pair,
lengths=[10, 50, 100],
num_samples=10,
)
for pair in layer
],
backend,
analysis=None,
)
for layer in layered_coupling_map
],
backend,
flatten_results=True,
analysis=None,
)
Mga katangian ng QPU sa paglipas ng panahonβ
Sa pagtingin sa mga inuulat na katangian ng QPU sa paglipas ng panahon (ikonsidera natin ang isang linggong panahon sa ibaba), nakikita natin kung paano ang mga ito ay maaaring mag-fluctuate sa sukat ng isang araw. Ang maliliit na pagbabago ay maaaring mangyari kahit sa loob ng isang araw. Sa sitwasyong ito, ang mga inuulat na katangian (na ina-update nang isang beses bawat araw) ay hindi eksakto na makakakuha ng kasalukuyang estado ng QPU. Bukod pa rito, kung ang isang gawain ay na-transpile nang lokal (gamit ang kasalukuyang mga inuulat na katangian) at naisumite ngunit naisagawa lamang sa mas huli pang oras (mga minuto o araw), maaari itong magkaroon ng panganib na gumamit ng mga lipas na katangian para sa pagpili ng qubit sa hakbang ng transpilation. Binibigyang-diin nito ang kahalagahan ng pagkakaroon ng napapanahong impormasyon tungkol sa QPU sa oras ng execution. Una, kunin natin ang mga katangian sa isang tiyak na hanay ng panahon.
instruction_2q_name = "cz" # set the name of the default 2q of the device
errors_list = []
for day_idx in range(10, 17):
calibrations_time = datetime(
year=2025, month=8, day=day_idx, hour=0, minute=0, second=0
)
targer_hist = backend.target_history(datetime=calibrations_time)
t1_dict, t2_dict = {}, {}
for qubit in range(targer_hist.num_qubits):
t1_dict[qubit] = targer_hist.qubit_properties[qubit].t1
t2_dict[qubit] = targer_hist.qubit_properties[qubit].t2
errors_dict = {
"1q": targer_hist["sx"],
"2q": targer_hist[f"{instruction_2q_name}"],
"spam": targer_hist["measure"],
"t1": t1_dict,
"t2": t2_dict,
}
errors_list.append(errors_dict)
Pagkatapos, i-plot natin ang mga halaga
fig, axs = plt.subplots(5, 1, figsize=(10, 20), sharex=False)
# Plot for T1 values
for qubit in range(targer_hist.num_qubits):
t1s = []
for errors_dict in errors_list:
t1_dict = errors_dict["t1"]
try:
t1s.append(t1_dict[qubit] / 1e-6)
except:
print(f"missing t1 data for qubit {qubit}")
axs[0].plot(t1s)
axs[0].set_title("T1")
axs[0].set_ylabel(r"Time ($\mu s$)")
axs[0].set_xlabel("Days")
# Plot for T2 values
for qubit in range(targer_hist.num_qubits):
t2s = []
for errors_dict in errors_list:
t2_dict = errors_dict["t2"]
try:
t2s.append(t2_dict[qubit] / 1e-6)
except:
print(f"missing t2 data for qubit {qubit}")
axs[1].plot(t2s)
axs[1].set_title("T2")
axs[1].set_ylabel(r"Time ($\mu s$)")
axs[1].set_xlabel("Days")
# Plot SPAM values
for qubit in range(targer_hist.num_qubits):
spams = []
for errors_dict in errors_list:
spam_dict = errors_dict["spam"]
spams.append(spam_dict[tuple([qubit])].error)
axs[2].plot(spams)
axs[2].set_title("SPAM Errors")
axs[2].set_ylabel("Error Rate")
axs[2].set_xlabel("Days")
# Plot 1Q Gate Errors
for qubit in range(targer_hist.num_qubits):
oneq_gates = []
for errors_dict in errors_list:
oneq_gate_dict = errors_dict["1q"]
oneq_gates.append(oneq_gate_dict[tuple([qubit])].error)
axs[3].plot(oneq_gates)
axs[3].set_title("1Q Gate Errors")
axs[3].set_ylabel("Error Rate")
axs[3].set_xlabel("Days")
# Plot 2Q Gate Errors
for pair in one_dir_coupling_map:
twoq_gates = []
for errors_dict in errors_list:
twoq_gate_dict = errors_dict["2q"]
twoq_gates.append(twoq_gate_dict[pair].error)
axs[4].plot(twoq_gates)
axs[4].set_title("2Q Gate Errors")
axs[4].set_ylabel("Error Rate")
axs[4].set_xlabel("Days")
plt.subplots_adjust(hspace=0.5)
plt.show()

Makikita ninyo na sa loob ng ilang araw, ang ilan sa mga katangian ng qubit ay maaaring magbago nang malaki. Binibigyang-diin nito ang kahalagahan ng pagkakaroon ng sariwang impormasyon tungkol sa estado ng QPU, upang mapili ang pinakamahusay na gumaganang mga qubit para sa isang eksperimento.
Hakbang 2: I-optimize ang problema para sa quantum hardware executionβ
Walang ginagawang optimization sa mga circuit o operator sa tutorial na ito.
Hakbang 3: Isagawa gamit ang mga Qiskit primitiveβ
Magsagawa ng quantum circuit gamit ang default na pagpili ng qubitβ
Bilang reference na resulta ng performance, isasagawa natin ang quantum circuit sa QPU gamit ang mga default na qubit, na siyang mga qubit na napili gamit ang hiniling na mga katangian ng backend. Gagamitin natin ang optimization_level = 3. Kasama sa setting na ito ang pinaka-advanced na optimization sa transpilation, at gumagamit ng mga katangian ng target (tulad ng mga operation error) upang piliin ang pinakamahusay na gumaganang mga qubit para sa execution.
pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)
isa_circuits = pm.run(circuits)
initial_qubits = [
[
idx
for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()
if qb._register.name != "ancilla"
]
for circuit in isa_circuits
]
Magsagawa ng quantum circuit gamit ang real-time na pagpili ng qubitβ
Sa seksyong ito, susuriin natin ang kahalagahan ng pagkakaroon ng napapanahong impormasyon sa mga katangian ng qubit ng QPU para sa optimal na mga resulta. Una, magsasagawa tayo ng kumpletong hanay ng mga eksperimento sa QPU characterization (, , SPAM, single-qubit RB at two-qubit RB), na maaari nating gamitin upang i-update ang mga katangian ng backend. Binibigyang-daan nito ang pass manager na pumili ng mga qubit para sa execution batay sa sariwang impormasyon tungkol sa QPU, na posibleng mapapabuti ang mga performance ng execution. Pangalawa, isasagawa natin ang Bell pair circuit at ihahambing ang fidelity na nakuha pagkatapos piliin ang mga qubit gamit ang na-update na mga katangian ng QPU sa fidelity na nakuha natin noon nang gumamit tayo ng default na mga inuulat na katangian para sa pagpili ng qubit.
Tandaan na ang ilan sa mga eksperimento sa characterization ay maaaring mabigo kapag ang routine sa fitting ay hindi makapag-fit ng curve sa nasukat na data. Kung makakakita kayo ng mga babala mula sa mga eksperimentong ito, suriin ang mga ito upang maunawaan kung aling characterization ang nabigo sa aling mga qubit, at subukang ayusin ang mga parameter ng eksperimento (tulad ng mga oras para sa , , o ang bilang ng mga haba ng mga RB experiment).
# Prepare characterization experiments
batches = [t1_exp, t2_exp, readout_exp, singleq_rb_exp, twoq_rb_exp_batched]
batches_exp = BatchExperiment(batches, backend) # , analysis=None)
run_options = {"shots": 1e3, "dynamic": False}
with Session(backend=backend) as session:
sampler = SamplerV2(mode=session)
# Run characterization experiments
batches_exp_data = batches_exp.run(
sampler=sampler, **run_options
).block_for_results()
EPG_sx_result_list = batches_exp_data.analysis_results("EPG_sx")
EPG_sx_result_q_indices = [
result.device_components.index for result in EPG_sx_result_list
]
EPG_x_result_list = batches_exp_data.analysis_results("EPG_x")
EPG_x_result_q_indices = [
result.device_components.index for result in EPG_x_result_list
]
T1_result_list = batches_exp_data.analysis_results("T1")
T1_result_q_indices = [
result.device_components.index for result in T1_result_list
]
T2_result_list = batches_exp_data.analysis_results("T2")
T2_result_q_indices = [
result.device_components.index for result in T2_result_list
]
Readout_result_list = batches_exp_data.analysis_results(
"Local Readout Mitigator"
)
EPG_2q_result_list = batches_exp_data.analysis_results(
f"EPG_{instruction_2q_name}"
)
# Update target properties
target = copy.deepcopy(backend.target)
for i in range(target.num_qubits - 1):
qarg = (i,)
if qarg in EPG_sx_result_q_indices:
target.update_instruction_properties(
instruction="sx",
qargs=qarg,
properties=InstructionProperties(
error=EPG_sx_result_list[i].value.nominal_value
),
)
if qarg in EPG_x_result_q_indices:
target.update_instruction_properties(
instruction="x",
qargs=qarg,
properties=InstructionProperties(
error=EPG_x_result_list[i].value.nominal_value
),
)
err_mat = Readout_result_list.value.assignment_matrix(i)
readout_assignment_error = (
err_mat[0, 1] + err_mat[1, 0]
) / 2 # average readout error
target.update_instruction_properties(
instruction="measure",
qargs=qarg,
properties=InstructionProperties(error=readout_assignment_error),
)
if qarg in T1_result_q_indices:
target.qubit_properties[i].t1 = T1_result_list[
i
].value.nominal_value
if qarg in T2_result_q_indices:
target.qubit_properties[i].t2 = T2_result_list[
i
].value.nominal_value
for pair_idx, pair in enumerate(one_dir_coupling_map):
qarg = tuple(pair)
try:
target.update_instruction_properties(
instruction=instruction_2q_name,
qargs=qarg,
properties=InstructionProperties(
error=EPG_2q_result_list[pair_idx].value.nominal_value
),
)
except:
target.update_instruction_properties(
instruction=instruction_2q_name,
qargs=qarg[::-1],
properties=InstructionProperties(
error=EPG_2q_result_list[pair_idx].value.nominal_value
),
)
# transpile circuits to updated target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
isa_circuit_updated = pm.run(circuits)
updated_qubits = [
[
idx
for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()
if qb._register.name != "ancilla"
]
for circuit in isa_circuit_updated
]
n_trials = 3 # run multiple trials to see variations
# interleave circuits
interleaved_circuits = []
for original_circuit, updated_circuit in zip(
isa_circuits, isa_circuit_updated
):
interleaved_circuits.append(original_circuit)
interleaved_circuits.append(updated_circuit)
# Run circuits
# Set simple error suppression/mitigation options
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
job_interleaved = sampler.run(interleaved_circuits * n_trials)
Hakbang 4: Mag-post-process at ibalik ang resulta sa nais na classical na formatβ
Sa wakas, ihambing natin ang fidelity ng Bell state na nakuha sa dalawang iba't ibang setting:
original, ibig sabihin ay sa mga default na qubit na pinili ng transpiler batay sa mga inuulat na katangian ng backend.updated, ibig sabihin ay sa mga qubit na pinili batay sa na-update na mga katangian ng backend pagkatapos magsagawa ng mga eksperimento sa characterization.
results = job_interleaved.result()
all_fidelity_list, all_fidelity_updated_list = [], []
for exp_idx in range(n_trials):
fidelity_list, fidelity_updated_list = [], []
for idx, num_qubits in enumerate(num_qubits_list):
pub_result_original = results[
2 * exp_idx * len(num_qubits_list) + 2 * idx
]
pub_result_updated = results[
2 * exp_idx * len(num_qubits_list) + 2 * idx + 1
]
fid = hellinger_fidelity(
ideal_dist, pub_result_original.data.c.get_counts()
)
fidelity_list.append(fid)
fid_up = hellinger_fidelity(
ideal_dist, pub_result_updated.data.c.get_counts()
)
fidelity_updated_list.append(fid_up)
all_fidelity_list.append(fidelity_list)
all_fidelity_updated_list.append(fidelity_updated_list)
plt.figure(figsize=(8, 6))
plt.errorbar(
num_qubits_list,
np.mean(all_fidelity_list, axis=0),
yerr=np.std(all_fidelity_list, axis=0),
fmt="o-.",
label="original",
color="b",
)
# plt.plot(num_qubits_list, fidelity_list, '-.')
plt.errorbar(
num_qubits_list,
np.mean(all_fidelity_updated_list, axis=0),
yerr=np.std(all_fidelity_updated_list, axis=0),
fmt="o-.",
label="updated",
color="r",
)
# plt.plot(num_qubits_list, fidelity_updated_list, '-.')
plt.xlabel("Chain length")
plt.xticks(num_qubits_list)
plt.ylabel("Fidelity")
plt.title("Bell pair fidelity at the edge of N-qubits chain")
plt.legend()
plt.grid(
alpha=0.2,
linestyle="-.",
)
plt.show()

Hindi lahat ng pagpapatakbo ay magpapakita ng pagpapabuti sa performance dahil sa real-time na characterization - at sa pagtaas ng haba ng chain, at sa gayon ay mas kaunting kalayaan na pumili ng mga pisikal na qubit, ang kahalagahan ng napapanahong impormasyon tungkol sa device ay nagiging mas hindi gaanong mahalaga. Gayunpaman, mabuting kasanayan na mangolekta ng sariwang data tungkol sa mga katangian ng device upang maunawaan ang performance nito. Paminsan-minsan, ang mga pansamantalang two-level system ay maaaring makaapekto sa performance ng ilan sa mga qubit. Ang real-time na data ay maaaring magpaalala sa atin kung kailan nangyayari ang gayong mga pangyayari at makatulong na maiwasan ang mga pagkabigo sa eksperimento sa mga pagkakataong ito.
Subukan na ilapat ang pamamaraang ito sa inyong mga execution at tukuyin kung gaano karaming pakinabang ang makukuha ninyo! Maaari din ninyong subukan at tingnan kung gaano karaming pagpapabuti ang makukuha ninyo mula sa iba't ibang backend.
Survey ng tutorialβ
Mangyaring sagutin ang maikling survey na ito upang magbigay ng feedback tungkol sa tutorial na ito. Ang inyong mga pananaw ay makakatulong sa amin na mapabuti ang aming mga alok sa nilalaman at karanasan ng user.