Lumaktaw sa pangunahing nilalaman

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)

Output of the previous code cell

Output of the previous code cell

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 T1T_1, T2T_2, 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 T1T_1 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 T1T_1 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 T2T_2 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% (1e\frac{1}{e}) ng initial amplitude nito dahil sa mga proseso ng dephasing decoherence. Sa isang T2T_2 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 (∣0⟩\vert 0 \rangle o ∣1⟩\vert 1 \rangle) 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()

Output of the previous code cell

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 (T1T_1, T2T_2, 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.

babala

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 T1T_1, T2T_2, 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()

Output of the previous code cell

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.

Call to action

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.

Link sa survey