Lumaktaw sa pangunahing nilalaman
Ang proyektong ito ay nasa beta. Nakakita ng bug? Mag-bukas ng GitHub issue. May tanong o ideya? Magtanong sa Discussions — gusto naming marinig ang iyong feedback!
Gaano kaganda ang kalidad ng pagsasalin sa pahinang ito?

I-benchmark ang QFT process fidelity gamit ang Orbit, isang Qiskit Function ng Quantum Elements

Tantya ng paggamit: 2 minuto sa isang Heron r3 processor. (TANDAAN: Tantya lamang ito. Maaaring mag-iba ang iyong runtime.) Bilang default, ang tutorial na ito ay nagsusumite ng isang Orbit Function job gamit ang server-side na IBM Quantum Compute Service Runtime Batch na may tatlong child job, 300 PUB bawat child job, at 921,600 shot sa kabuuan.

Babala: Ang mga dynamic circuit sa kasalukuyan ay isang eksperimental na feature, at napapailalim ang mga ito sa mga limitasyon sa Quantum Compute [3] na maaaring magdulot ng pagkabigo ng job. Halimbawa, ang error 6073 ay nagpapahiwatig na lumampas ang isang job sa memory limit ng classical-control hardware [4]. Binabawasan ng notebook na ito ang panganib na iyon sa pamamagitan ng pagpartisyon ng mga circuit size sa tatlong Runtime Batch child job [5]. Ang bawat fixed-size comparison ay nananatili sa isang child job, habang ang malalaki at maliliit na size ay ipinapares upang balansehin ang classical-control workload ng mga job.

Mga learning outcome​

  • Ihanda ang mga product state QFT†∣x⟩\mathrm{QFT}^\dagger|x\rangle na ginagamit ng sampled process-fidelity estimator sa Figure 2a ng Ref. [1].

  • Bumuo ng katumbas na unitary at dynamic na implementasyon ng quantum Fourier transform na sinusundan ng measurement.

  • Pumili ng physical qubit para sa mga dynamic circuit gamit ang kasalukuyang calibration at connectivity data.

  • Ihambing ang raw unitary, raw dynamic, at Orbit-enhanced dynamic QFT process-fidelity estimate habang lumalaki ang circuit size.

  • Magsumite ng isang Orbit workload sa pamamagitan ng batch mode API habang pinapanatili ang bawat fixed-size three-strategy comparison sa isang child job.

  • Suriin ang Orbit metadata upang kumpirmahin kung inilapat ang dynamical decoupling (DD) at measurement-error mitigation (MEM).

Background​

Ang Figure 2a ng Ref. [1] ay nagbe-benchmark ng process fidelity ng ideal na QFT+M channel laban sa noisy unitary at dynamic implementations. Para sa isang sampled computational-basis label xx, inihahanda ng benchmark ang QFT†∣x⟩\mathrm{QFT}^\dagger|x\rangle, inilalapat ang noisy QFT+M implementation, at tinatantya ang probability pxp_x ng pagkuha ng katumbas na ideal na output. Ang mga inverse-QFT state na ito ay separable at maaaring ihanda nang episyente gamit ang mga Hadamard gate at virtual phase rotation.

Para sa mm na mga independiyenteng sampled label, ginagamit ng notebook ang unbiased estimator na hinango sa Ref. [1]:

F^proc=mm−1(1m∑ℓ=1mpxℓ)2−1m(m−1)∑ℓ=1mpxℓ.\widehat{\mathcal{F}}_{\mathrm{proc}} = \frac{m}{m-1}\left(\frac{1}{m}\sum_{\ell=1}^{m}\sqrt{p_{x_\ell}}\right)^2 - \frac{1}{m(m-1)}\sum_{\ell=1}^{m}p_{x_\ell}.

Pinapalitan ng dynamic construction ang mga controlled-phase gate ng unitary QFT+M ng mga mid-circuit measurement at classically conditioned na phase rotation [1]. Dahil sa deferred measurement, pareho ang ideal na output distribution ng dalawang circuit. Nangangailangan ang unitary implementation ng all-to-all na two-qubit-gate, at dahil sa mga limitasyon ng local connectivity, nagkakaroon ito ng O(n2)O(n^2) na SWAP gate overhead. Sa kabilang banda, gumagamit lang ang dynamic circuit ng single qubit gate at O(n)O(n) na mid-circuit measurement na nagdudulot ng mahahabang idle sa gitna. Kung isasantabi nang lubusan ang DD, ang pangunahing labanan ay sa pagitan ng O(n2)O(n^2) na maiingay na unitary gate at O(n)O(n) na maiingay na mid-circuit measurement. Basta't sapat na mababa ang error ng mga idle habang isinasagawa ang mga mid-circuit measurement na ito, mas mahusay ang dynamic circuit kaysa sa mga unitary implementation, at ipinapakita naming makakamit ito gamit ang Orbit sa ibaba.

Relasyon sa Figure 2a. Sinusundan ng notebook na ito ang sampled process-fidelity protocol ng papel, ngunit ito ay isang Orbit-focused na adaptasyon ng tutorial sa halip na isang reproduction. Halimbawa, kung saan ginamit ng Figure 2a ang ibm_kyiv na may 2000 shot, gumagamit tayo ng modernong device na ibm_aachen na may mas maliit na 1024-shot count upang matipid ang oras ng QPU.

Mga halimbawang resulta​

Ipinapakita ng static plot sa ibaba ang mean process-fidelity curve mula sa tatlong magkakasunod na development job na pinatakbo sa ibm_aachen gamit ang prosesong inilarawan sa ibaba. Gaya ng ipinapakita rito, makabuluhang mapapataas ng Orbit ang kalidad ng mga dynamic circuit; ang dynamic QFT ay tumutugma sa published benchmark quality at nagpapakita ng pagpapabuti kumpara sa standard unitary QFT. Gaya ng makikita natin, ang mga resultang ito ay nagmumula sa isang automated na mahusay na pagpili ng mga qubit, automatic na pagpasok ng dynamic decoupling (hindi hand-optimized para sa problemang ito), at measurement error mitigation. Para masaya, ihambing mo ang mga resultang ito sa sarili mong resulta sa dulo, lalo na kung pumili ka ng ibang backend.

Tandaan: Ang mga resultang ito ay isang illustrative snapshot ng isang matagumpay na naunang run gamit ang Orbit, hindi isang garantiya ng performance. Ang mga resulta sa ibaba ay dapat magmukhang qualitatively kahalintulad, ngunit ang mga detalye ay nakadepende sa napiling device at sa mga katangian nito, lalo na ang mga error sa measurement at idling, sa oras ng pagpapatakbo. QFT process fidelity on 'ibm_aachen'

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_catalog": "qiskit-ibm-catalog", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")

Requirements​

I-install ang pinakabagong bersyon ng mga sumusunod na package bago patakbuhin ang tutorial na ito:

  • numpy

  • matplotlib

  • qiskit

  • qiskit-ibm-runtime

  • qiskit-ibm-catalog

pip install qiskit qiskit-ibm-runtime qiskit-ibm-catalog numpy matplotlib

Setup​

Mag-authenticate gamit ang IBM Quantum® Platform, i-load ang ibm_aachen, at i-load ang Quantum Elements Orbit mula sa Qiskit Functions Catalog.

Ang N_VALUES ay ang listahan ng mga laki ng circuit: ang bawat value ay ang bilang ng mga logical qubit sa isang QFT circuit. Ang M ay ang bilang ng mga independiyenteng sampled na basis label na xx na sinusuri para sa bawat laki; sa fidelity estimator, ito ang mm sa equation sa itaas. Para sa bawat pares na (N, x), nagsusumite ang notebook ng isang PUB para sa bawat isa sa tatlong estratehiya, kaya ang isang fixed na laki ay nag-aambag ng 3 * M PUB at 3 * M * SHOTS circuit shot.

Kaya ang default na sweep ay sumusuri ng 15 laki ng circuit, 20 sampled na bitstring bawat laki, at tatlong estratehiya. Tinutukoy ng NUM_BATCH_PARTITIONS=3 ang tatlong ordered na Runtime Batch partition, at ipinapasa ang BATCH_MAX_PUBS_PER_JOB sa pamamagitan ng orbit.run(...) bilang JSON configuration. Ang malalaking N_VALUES ay lumilikha ng mas kumplikadong dynamic circuit, habang ang mas malaking M ay naglalagay ng mas maraming PUB sa bawat child job; alinman sa dalawa ay maaaring magpataas ng pressure sa classical-control memory ng backend. Kung umabot sa limitasyong iyon ang isang indibidwal na dynamic-circuit child job, bawasan ang N_VALUES o M, o dagdagan ang NUM_BATCH_PARTITIONS. Bawasan ang SHOTS kapag ang layunin ay babaan ang execution usage sa halip na ang bilang o pagiging kumplikado ng mga circuit.

Hinihiling ng OPTIMIZATION_LEVEL=0 sa transpiler ng Qiskit na gawin ang kinakailangang translation at routing nang may minimal na optimization. Ginagamit namin ito para mapanatili ang tahasang pinili na mga dynamic-circuit layout at para mas madaling bigyang-kahulugan ang mga raw na paghahambing. Wastong mga eksperimento ang Levels 1–3, ngunit maaari nilang isulat muli o pasimplehin ang mga circuit at baguhin ang napiling placement; kaunti lang ang inaasahang benepisyo nito rito dahil walang two-qubit gate o limitasyon sa connectivity ang dynamic circuit. Kung babaguhin mo ang level, patakbuhin muli ang benchmark at ituring na hiwalay na paghahambing ang mga resulta.

import warnings
from collections import Counter, defaultdict

import matplotlib.pyplot as plt
import numpy as np
from qiskit import (
ClassicalRegister,
QuantumCircuit,
QuantumRegister,
transpile,
)
from qiskit.circuit import IfElseOp
from qiskit.synthesis.qft import synth_qft_full
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_ibm_runtime import QiskitRuntimeService

IBM_BACKEND_NAME = "ibm_marrakesh" # Change to your preferred backend

# N_VALUES contains circuit sizes in logical qubits; M is samples per size.
N_VALUES = [2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40]
M = 20
# Each sample is run with this many shots for each of the three strategies.
SHOTS = 1024
# Transpilation options.
OPTIMIZATION_LEVEL = 0 # Level 0 preserves the explicitly selected layouts and dynamic structure.
RNG_SEED = 12345 # Reproducible transpilation seed.
# Batch partitioning options.
NUM_BATCH_PARTITIONS = 3
STRATEGY_LABELS = ("unitary/raw", "dynamic/raw", "dynamic/orbit")

def balanced_n_groups(
n_values: list[int], num_jobs: int = 3
) -> list[list[int]]:
"""Pair circuit sizes into balanced Runtime Batch partitions."""
values = sorted(n_values)
if len(set(values)) != len(values):
raise ValueError("N_VALUES must not contain duplicates")
if not 1 <= num_jobs <= len(values):
raise ValueError(
"NUM_BATCH_PARTITIONS must be between 1 and len(N_VALUES)"
)

max_group_size = (len(values) + num_jobs - 1) // num_jobs
groups = [[] for _ in range(num_jobs)]
loads = [0] * num_jobs
pair_counts = [0] * num_jobs
remaining = values.copy()

while len(remaining) >= 2:
candidates = [
i
for i, group in enumerate(groups)
if len(group) + 2 <= max_group_size
]
if not candidates:
break
smallest = remaining.pop(0)
largest = remaining.pop()
job_index = min(
candidates, key=lambda i: (loads[i], len(groups[i]), i)
)
pair = (
[largest, smallest]
if pair_counts[job_index] % 2 == 0
else [smallest, largest]
)
groups[job_index].extend(pair)
loads[job_index] += smallest + largest
pair_counts[job_index] += 1

while remaining:
value = remaining.pop()
candidates = [
i for i, group in enumerate(groups) if len(group) < max_group_size
]
job_index = min(
candidates, key=lambda i: (loads[i], len(groups[i]), i)
)
groups[job_index].append(value)
loads[job_index] += value

return groups

N_GROUPS = balanced_n_groups(N_VALUES, NUM_BATCH_PARTITIONS)

service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.backend(IBM_BACKEND_NAME)
if "if_else" not in backend.target.operation_names:
backend.target.add_instruction(IfElseOp, name="if_else")

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
quantum_elements_orbit = catalog.load("quantum-elements/orbit")
if quantum_elements_orbit is None:
raise RuntimeError(
"Quantum Elements Orbit is not enabled for this IBM Quantum instance."
)

required_qubits = max(N_VALUES)
if backend.num_qubits < required_qubits:
raise ValueError(
f"Backend {backend.name} has {backend.num_qubits} qubits, "
f"but this benchmark needs at least {required_qubits}."
)

{
"orbit version": quantum_elements_orbit.version,
"backend": backend.name,
"num_qubits": backend.num_qubits,
"n_values": N_VALUES,
"m": M,
"shots": SHOTS,
"num_function_jobs": 1,
"num_runtime_batch_partitions": NUM_BATCH_PARTITIONS,
"n_groups": N_GROUPS,
"pubs_per_partition": [
len(group) * M * len(STRATEGY_LABELS) for group in N_GROUPS
],
"total_pubs": len(N_VALUES) * M * len(STRATEGY_LABELS),
"total_shots": len(N_VALUES) * M * len(STRATEGY_LABELS) * SHOTS,
}
{'orbit version': '0.3.23',
'backend': 'ibm_marrakesh',
'num_qubits': 156,
'n_values': [2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40],
'm': 20,
'shots': 1024,
'num_function_jobs': 1,
'num_runtime_batch_partitions': 3,
'n_groups': [[40, 2, 7, 15, 10], [35, 3, 6, 20, 9], [30, 4, 5, 25, 8]],
'pubs_per_partition': [300, 300, 300],
'total_pubs': 900,
'total_shots': 921600}

Bumuo ng mga QFT circuit​

Para sa bawat sampled integer na xx, inihahanda ng bit_inv_qft ang product state QFT†∣x⟩\mathrm{QFT}^\dagger|x\rangle gamit ang mga Hadamard na sinusundan ng phase rotation. Pagkatapos, idinaragdag ng notebook ang alinman sa standard unitary QFT o ang katumbas nitong semiclassical dynamic QFT.

Parehong hindi isinasama ng dalawang implementasyon ang huling swap network. Kaya naman, dahil sa classical-bit display order ng Qiskit, ang inaasahang measured string ay ang baligtad ng zero-padded binary representation ng xx, na naka-encode ng format(x, f"0{n}b")[::-1].

def bit_inv_qft(circuit: QuantumCircuit, x: int, conv: str = "LSB") -> None:
"""Prepare QFT†|x⟩ as a product state on `circuit`."""
num_qubits = circuit.num_qubits
circuit.h(range(num_qubits))
for j in range(num_qubits):
phase = (
2 * np.pi * x / 2 ** (num_qubits - j)
if conv == "LSB"
else 2 * np.pi * x / 2 ** (j + 1)
)
circuit.p(-phase, j)

def build_unitary_qft_circuit(num_qubits: int, x: int) -> QuantumCircuit:
"""Build a unitary QFT circuit for sampled basis label `x`."""
if not 0 <= x < 2**num_qubits:
raise ValueError(
f"x={x} is outside the {num_qubits}-qubit basis range"
)
qreg = QuantumRegister(num_qubits, "q")
creg = ClassicalRegister(num_qubits, "c")
circuit = QuantumCircuit(qreg, creg, name=f"unitary_qft_{num_qubits}q")
bit_inv_qft(circuit, x)
circuit.append(
synth_qft_full(num_qubits, do_swaps=False), range(num_qubits)
)
circuit.measure(range(num_qubits), range(num_qubits))
return circuit

def _warn_if_precision_loss(max_num_entanglements: int) -> None:
"""Warn when the smallest QFT rotation may lose float precision."""
if max_num_entanglements > -np.finfo(float).minexp:
warnings.warn(
"precision loss in QFT."
f" The rotation needed to represent {max_num_entanglements} entanglements"
" is smaller than the smallest normal floating-point number.",
category=RuntimeWarning,
stacklevel=4,
)

def synth_dynamic_qft(
circuit: QuantumCircuit, *, do_swaps: bool = False
) -> QuantumCircuit:
"""Append semiclassical dynamic QFT operations to `circuit`."""
num_qubits = circuit.num_qubits
creg = circuit.cregs[0]
_warn_if_precision_loss(num_qubits - 1)

for j in reversed(range(num_qubits)):
circuit.h(j)
circuit.measure([j], [j])

if j > 0:
with circuit.if_test((creg[j], 1)):
for k in reversed(range(j)):
circuit.p(np.pi * (2.0 ** (k - j)), k)

if do_swaps:
for i in range(num_qubits // 2):
circuit.swap(i, num_qubits - i - 1)
return circuit

def build_dynamic_qft_circuit(num_qubits: int, x: int) -> QuantumCircuit:
"""Build a dynamic QFT circuit for sampled basis label `x`."""
if not 0 <= x < 2**num_qubits:
raise ValueError(
f"x={x} is outside the {num_qubits}-qubit basis range"
)
qreg = QuantumRegister(num_qubits, "q")
creg = ClassicalRegister(num_qubits, "c")
circuit = QuantumCircuit(qreg, creg, name=f"dynamic_qft_{num_qubits}q")
bit_inv_qft(circuit, x)
synth_dynamic_qft(circuit, do_swaps=False)
return circuit

def target_output_bitstring(x: int, n_qubits: int) -> str:
"""Return Qiskit's expected measured bitstring for basis label `x`."""
return format(int(x), f"0{n_qubits}b")[::-1]

def process_fidelity_from_success_probabilities(
success_probabilities: list[float],
) -> float:
"""Estimate process fidelity from sampled success probabilities."""
m = len(success_probabilities)
if m <= 1:
raise ValueError(
"m must be larger than 1 for the process-fidelity estimator"
)
succ = np.asarray(success_probabilities, dtype=float)
return float(
(m / (m - 1)) * (np.mean(np.sqrt(succ)) ** 2)
- np.sum(succ) / (m * (m - 1))
)

Pumili ng dynamic-circuit physical qubits​

Hindi kinakailangan ng dynamic implementation ang mga two-qubit gate, kaya hindi kailangang bumuo ng connected subgraph ang mga physical qubit nito. Para sa bawat circuit size, iniraranggo ng selector ang kasalukuyang mga backend qubit gamit ang isang score na 80% nakatuon sa mas mababang readout error at 10% bawat isa sa mas mataas na T1T_1 at T2T_2. Una itong pumipili ng mga high-scoring qubit na walang direktang coupling sa isa't isa kung maaari, na maaaring magbawas ng exposure sa nearest-neighbor crosstalk, at pagkatapos ay pinupunan ang anumang natitirang posisyon ayon sa score.

Ginagamit ng mga variant na dynamic/raw at dynamic/orbit ang eksaktong parehong napiling layout para sa isang partikular na size, kaya layout-controlled ang paghahambing sa kanila. Ang unitary/raw circuit ay sa halip ay ini-map at rina-route ng transpiler dahil kailangan nito ng two-qubit connectivity. Ang runtime calibration-based na pagpiling ito ay tiyak sa tutorial na ito; hindi ito ang fixed 40-qubit na ibm_kyiv layout na ginamit para sa mga eksperimento sa Figure 2a ng papel.

def value_from_property(raw):
"""Extract a numeric value from a backend property record."""
if raw is None:
return None
if isinstance(raw, tuple):
return raw[0]
return getattr(raw, "value", raw)

def qubit_property_value(properties, qubit: int, *names: str) -> float | None:
"""Read the first available backend property for one qubit."""
for name in names:
try:
value = value_from_property(
properties.qubit_property(qubit, name)
)
except Exception:
value = None
if value is not None:
return float(value)
return None

def measurement_error(properties, qubit: int) -> float | None:
"""Return readout error, deriving it from asymmetric errors if needed."""
readout = qubit_property_value(properties, qubit, "readout_error")
if readout is not None:
return readout
p01 = qubit_property_value(properties, qubit, "prob_meas0_prep1")
p10 = qubit_property_value(properties, qubit, "prob_meas1_prep0")
if p01 is not None and p10 is not None:
return 0.5 * (p01 + p10)
return None

def coupling_edges(backend) -> list[tuple[int, int]]:
"""Return directed coupling-map edges as integer pairs."""
coupling_map = getattr(backend, "coupling_map", None)
if coupling_map is not None:
try:
return [(int(a), int(b)) for a, b in coupling_map.get_edges()]
except Exception:
pass
built = backend.target.build_coupling_map()
return [(int(a), int(b)) for a, b in built.get_edges()]

def neighbor_map(backend) -> dict[int, set[int]]:
"""Build an undirected neighboring-qubit map from backend connectivity."""
neighbors = {qubit: set() for qubit in range(backend.num_qubits)}
for a, b in coupling_edges(backend):
neighbors[a].add(b)
neighbors[b].add(a)
return neighbors

def anchored_score(
value: float | None, *, good: float, bad: float, higher_is_better: bool
) -> float:
"""Normalize a calibration value to a [0, 1] quality score."""
if value is None:
return 0.0
if higher_is_better:
low, high = sorted((bad, good))
score = (value - low) / (high - low)
else:
low, high = sorted((good, bad))
score = (high - value) / (high - low)
return float(min(1.0, max(0.0, score)))

def qubit_metrics(backend) -> list[dict]:
"""Collect and rank calibration metrics for all backend qubits."""
properties = backend.properties()
rows = []
for qubit in range(backend.num_qubits):
t1 = qubit_property_value(properties, qubit, "T1", "t1")
t2 = qubit_property_value(properties, qubit, "T2", "t2")
meas_error = measurement_error(properties, qubit)
measurement_score = anchored_score(
meas_error, good=0.005, bad=0.05, higher_is_better=False
)
t1_score = anchored_score(
t1, good=0.00025, bad=0.00005, higher_is_better=True
)
t2_score = anchored_score(
t2, good=0.00025, bad=0.00005, higher_is_better=True
)
rows.append(
{
"qubit": qubit,
"t1": t1,
"t2": t2,
"measurement_error": meas_error,
"score": 0.8 * measurement_score
+ 0.1 * t1_score
+ 0.1 * t2_score,
}
)
return sorted(rows, key=lambda row: row["score"], reverse=True)

def select_dynamic_qubits(backend, n_qubits: int) -> list[int]:
"""Choose high-scoring, preferably nonadjacent qubits for dynamic circuits."""
ranked = qubit_metrics(backend)
neighbors = neighbor_map(backend)
selected = []
blocked = set()
for row in ranked:
qubit = row["qubit"]
if qubit in blocked:
continue
selected.append(qubit)
blocked.add(qubit)
blocked.update(neighbors.get(qubit, set()))
if len(selected) == n_qubits:
return selected

for row in ranked:
qubit = row["qubit"]
if qubit not in selected:
selected.append(qubit)
if len(selected) == n_qubits:
return selected
raise RuntimeError(f"Could not select {n_qubits} physical qubits")

print("The top 3 qubits (according to our scoring): ")
print(qubit_metrics(backend)[0:3])
print("Worst 3 qubits (according to our scoring): ")
print(qubit_metrics(backend)[-3:])
The top 3 qubits (according to our scoring):
[{'qubit': 35, 't1': 0.0004024167194138543, 't2': 0.0003811132194981602, 'measurement_error': 0.003662109375, 'score': 1.0}, {'qubit': 92, 't1': 0.0003281277199162346, 't2': 0.0002414989126012369, 'measurement_error': 0.00390625, 'score': 0.9957494563006185}, {'qubit': 5, 't1': 0.0002919284700896914, 't2': 0.0002184196315970577, 'measurement_error': 0.00537109375, 'score': 0.9776125935763066}]
Worst 3 qubits (according to our scoring):
[{'qubit': 76, 't1': 7.65424382656864e-05, 't2': 3.438638139929193e-05, 'measurement_error': 0.05859375, 'score': 0.013271219132843196}, {'qubit': 26, 't1': 7.000219049917298e-06, 't2': 7.849466281257113e-06, 'measurement_error': 0.1009521484375, 'score': 0.0}, {'qubit': 82, 't1': 7.260327905277343e-06, 't2': 6.440762177221646e-06, 'measurement_error': 0.5048828125, 'score': 0.0}]

Ihanda ang mga benchmark PUB​

Para sa bawat pares na (N, x), tinatranspile muna ng notebook ang mga logical circuit at gumagawa ng isang Sampler PUB para sa bawat estratehiya:

  • unitary/raw: unitary QFT sa napiling layout ng transpiler, na may routing kung kinakailangan at walang Orbit DD o MEM.

  • dynamic/raw: dynamic QFT sa mga physical qubit na napili ayon sa calibration, walang Orbit DD o MEM.

  • dynamic/orbit: ang parehong transpiled dynamic circuit sa parehong physical qubit, na may Orbit DD at MEM na naka-enable.

Ginagamit ng Orbit-enhanced na PUB ang transpilation_mode="validate" dahil napili na ang mapping nito. Ini-validate ng Orbit ang ibinigay na physical circuit sa halip na i-remap ito, pagkatapos ay inilalapat ang DD at MEM pipeline nito. Dahil ang enhanced dynamic curve lamang ang humihiling ng MEM, hindi ito dapat ituring bilang isang isolated na paghahambing ng DD kumpara sa walang-DD.

Pinapatag ang mga PUB, per-PUB na opsyon, at mga record ng resulta sa parehong pagkakasunod-sunod. Para sa bawat fixed na NN, ang mga PUB na unitary/raw, dynamic/raw, at dynamic/orbit ay pinapanatiling magkakasama sa isang Runtime Batch partition. Ipinapares ng grouping helper ang malalaki at maliliit na circuit size, pinagpapalit-palit ang kanilang pagkakasunod-sunod, at binabalanse ang kabuuan ng NN sa tatlong partition bilang isang simpleng proxy para sa classical-control workload.

strategy_options = {
"unitary/raw": {"mode": "raw"},
"dynamic/raw": {"mode": "raw"},
"dynamic/orbit": {"mode": "orbit", "transpilation_mode": "validate"},
}
rng = np.random.default_rng(RNG_SEED)
pubs = []
pub_options = []
pub_records = []
layout_summary = {}

target_decimals_by_n = {
n_qubits: [int(x) for x in rng.integers(0, 2**n_qubits, size=M)]
for n_qubits in N_VALUES
}

for partition_index, n_group in enumerate(N_GROUPS):
for n_qubits in n_group:
dynamic_qubits = select_dynamic_qubits(backend, n_qubits)
layout_summary[str(n_qubits)] = {"dynamic_qubits": dynamic_qubits}

for x in target_decimals_by_n[n_qubits]:
target_bitstring = target_output_bitstring(x, n_qubits)
unitary_logical = build_unitary_qft_circuit(n_qubits, x)
dynamic_logical = build_dynamic_qft_circuit(n_qubits, x)

unitary_transpiled = transpile(
unitary_logical,
backend=backend,
optimization_level=OPTIMIZATION_LEVEL,
seed_transpiler=RNG_SEED,
)
dynamic_transpiled = transpile(
dynamic_logical,
backend=backend,
optimization_level=OPTIMIZATION_LEVEL,
seed_transpiler=RNG_SEED,
initial_layout=dynamic_qubits,
)

circuits_by_label = {
"unitary/raw": unitary_transpiled,
"dynamic/raw": dynamic_transpiled,
"dynamic/orbit": dynamic_transpiled,
}
for label in STRATEGY_LABELS:
circuit = circuits_by_label[label]
options = dict(strategy_options[label])
pubs.append((circuit, None, SHOTS))
pub_options.append(options)
pub_records.append(
{
"partition_index": partition_index,
"n_qubits": n_qubits,
"target_decimal": x,
"target_bitstring": target_bitstring,
"label": label,
"pub_options": options,
"dynamic_qubits": (
dynamic_qubits
if label.startswith("dynamic/")
else None
),
"transpiled_depth": circuit.depth(),
"transpiled_size": circuit.size(),
}
)

BATCH_MAX_PUBS_PER_JOB = max(
len(group) * M * len(STRATEGY_LABELS) for group in N_GROUPS
)

{
"num_function_jobs": 1,
"num_runtime_batch_partitions": len(N_GROUPS),
"n_groups": {
job_index: group for job_index, group in enumerate(N_GROUPS)
},
"n_load_per_job": {
job_index: sum(group) for job_index, group in enumerate(N_GROUPS)
},
"pubs_per_partition": {
partition_index: len(group) * M * len(STRATEGY_LABELS)
for partition_index, group in enumerate(N_GROUPS)
},
"expected_executions_per_partition": {
partition_index: len(group) * M * len(STRATEGY_LABELS) * SHOTS
for partition_index, group in enumerate(N_GROUPS)
},
"first_pub_record": pub_records[0],
"largest_dynamic_qubit_set": layout_summary[str(max(N_VALUES))][
"dynamic_qubits"
],
}
{'num_function_jobs': 1,
'num_runtime_batch_partitions': 3,
'n_groups': {0: [40, 2, 7, 15, 10],
1: [35, 3, 6, 20, 9],
2: [30, 4, 5, 25, 8]},
'n_load_per_job': {0: 74, 1: 73, 2: 72},
'pubs_per_partition': {0: 300, 1: 300, 2: 300},
'expected_executions_per_partition': {0: 307200, 1: 307200, 2: 307200},
'first_pub_record': {'partition_index': 0,
'n_qubits': 40,
'target_decimal': 853235401719,
'target_bitstring': '1110111111000000110100110001010101100011',
'label': 'unitary/raw',
'pub_options': {'mode': 'raw'},
'dynamic_qubits': None,
'transpiled_depth': 4778,
'transpiled_size': 28259},
'largest_dynamic_qubit_set': [35,
92,
5,
141,
75,
147,
1,
152,
12,
10,
128,
107,
21,
8,
15,
98,
33,
134,
154,
53,
78,
109,
112,
149,
120,
55,
3,
132,
85,
49,
115,
101,
95,
64,
136,
71,
90,
56,
18,
44]}

Patakbuhin ang benchmark​

Ipasa ang JSON-serializable na Batch configuration sa pamamagitan ng orbit.run(...). Ang Orbit ang lumilikha ng authenticated na Runtime Batch sa loob ng Function, nagsusumite ng mga ordered na PUB partition, nagsasara ng Batch pagkatapos ng pagsusumite, at nagbabalik ng isang pinagsamang resulta sa caller. Hindi makokontrol ng lokal na Batch na nilikha ng caller ang isang asynchronous na Qiskit Function job.

Ang ordered na PUB list ay pinapartisyon ayon sa mga grupo ng bilang ng qubit para maihambing nang patas ang lahat ng tatlong estratehiya para sa isang fixed na NN: ang lahat ng 60 PUB para sa isang fixed na NN — 20 sampled input na pinarami sa tatlong estratehiya — ay nag-eexecute sa iisang Runtime Batch child job. Pinagsasama ng default na mga grupo ang malaki at maliit na circuit at naglalaman ng 300 PUB bawat isa, na binabawasan ang tsansang maipon sa isang child job ang lahat ng pinakamalalaking dynamic program habang pinapanatili ang mga within-job na paghahambing.

job = quantum_elements_orbit.run(
primitive="sampler",
pubs=pubs,
backend_name=backend.name,
options={
"pub_options": pub_options,
"batch": {
"max_pubs_per_job": BATCH_MAX_PUBS_PER_JOB,
"max_time": "2h",
},
"save_backend_info": True,
},
)

{
"backend": backend.name,
"function_job_id": job.job_id,
"status": job.status(),
"num_pubs": len(pubs),
"max_pubs_per_runtime_job": BATCH_MAX_PUBS_PER_JOB,
"n_groups": N_GROUPS,
}
{'backend': 'ibm_marrakesh',
'function_job_id': '4b953302-afc3-4889-9ecf-e63d78b4e252',
'status': 'QUEUED',
'num_pubs': 900,
'max_pubs_per_runtime_job': 300,
'n_groups': [[40, 2, 7, 15, 10], [35, 3, 6, 20, 9], [30, 4, 5, 25, 8]]}

Kunin ang mga resulta at kalkulahin ang process fidelity​

Kunin ang nag-iisang pinagsamang resulta na ibinalik ng Orbit Function. Itinatala ng Orbit ang Runtime Batch ID, mga child Runtime job ID, at mga PUB range sa result metadata; napapanatili ang pagkakasunod-sunod ng PUB sa lahat ng child job. Para sa bawat PUB, ang pxp_x ay ang probability na naka-assign sa inaasahang bitstring. Binabasa ng extract_counts ang mga count na ibinalik sa caller; para sa dynamic/orbit, ito ang mga MEM-adjusted na count kapag matagumpay ang mitigation. Nakukuha rin ng extract_raw_counts ang katumbas na unmitigated na count na naitala sa Orbit metadata. Pinagsasama-sama ng code ang 20 halaga ng pxp_x para sa bawat pares na (N, label) at inilalapat ang estimator na ipinakilala sa itaas.

Kaya naman, ang naka-plot na process_fidelity dictionary ay gumagamit ng raw count para sa unitary/raw at dynamic/raw, ngunit MEM-adjusted na count para sa dynamic/orbit. Ang katapat na raw_process_fidelity dictionary ay nagtatabi ng unmitigated na kalkulasyon para sa bawat estratehiya at kapaki-pakinabang kapag pinaghihiwalay ang epekto ng MEM mula sa iba pang bahagi ng Orbit pipeline. Itinutuwid ng MEM ang naibalik na output histogram; hindi nito magagawang baguhin nang retroactive ang isang mid-circuit measurement result na nagamit na ng real-time feedforward.

def extract_counts(pub_result) -> dict[str, int]:
"""Extract the first nonempty classical-register counts mapping."""
data = getattr(pub_result, "data", None)
if data is None:
raise TypeError("pub_result.data is missing")

for name in dir(data):
if name.startswith("_"):
continue
register = getattr(data, name)
get_counts = getattr(register, "get_counts", None)
if callable(get_counts):
counts = get_counts()
if counts:
return counts

raise TypeError(
"No classical register with get_counts() found in pub_result.data"
)

def extract_raw_counts(pub_result) -> dict[str, int]:
"""Extract unmitigated counts from Orbit metadata when available."""
orbit_metadata = pub_result.metadata.get("quantum_elements_orbit", {})
mem_report = orbit_metadata.get("measurementErrorMitigation", {})
return mem_report.get("rawCounts") or extract_counts(pub_result)

def probability_for_bitstring(
counts: dict[str, int], bitstring: str, n_qubits: int
) -> float:
"""Return observed probability of an expected bitstring."""
total = sum(counts.values())
if total <= 0:
return 0.0
normalized = Counter()
for measured, count in counts.items():
key = measured.replace(" ", "")[-n_qubits:].zfill(n_qubits)
normalized[key] += count
return float(normalized.get(bitstring, 0) / total)

try:
job_result = job.result()
except Exception as exc:
job_logs = getattr(job, "logs", lambda: "")()
if job_logs:
print(f"Logs for Function job {job.job_id}:\n{job_logs}")
raise RuntimeError(
f"Batched Orbit Function job {job.job_id} failed: "
f"{type(exc).__name__}: {exc}"
) from exc

if len(job_result) != len(pub_records):
raise RuntimeError(
f"Function job {job.job_id} returned {len(job_result)} PUB results; "
f"expected {len(pub_records)}"
)

batch_report = job_result.metadata.get("quantum_elements_orbit", {}).get(
"batch", {}
)
batch_partitions = batch_report.get("partitions", [])
partition_by_pub = {}
for partition in batch_partitions:
start = int(partition["pubStart"])
stop = start + int(partition["pubCount"])
for pub_index in range(start, stop):
partition_by_pub[pub_index] = partition

if len(partition_by_pub) != len(pub_records):
raise RuntimeError(
"Orbit Batch metadata does not cover every returned PUB."
)

grouped_success = defaultdict(list)
grouped_raw_success = defaultdict(list)
pub_summaries = []

for pub_index, (record, pub_result) in enumerate(
zip(pub_records, job_result, strict=True)
):
partition = partition_by_pub[pub_index]
label = record["label"]
n_qubits = record["n_qubits"]
counts = extract_counts(pub_result)
raw_counts = extract_raw_counts(pub_result)
success = probability_for_bitstring(
counts, record["target_bitstring"], n_qubits
)
raw_success = probability_for_bitstring(
raw_counts, record["target_bitstring"], n_qubits
)
key = (n_qubits, label)
grouped_success[key].append(success)
grouped_raw_success[key].append(raw_success)

orbit_report = pub_result.metadata.get("quantum_elements_orbit", {})
mem_report = orbit_report.get("measurementErrorMitigation", {})
pub_summaries.append(
{
**record,
"function_job_id": job.job_id,
"runtime_batch_id": batch_report.get("id"),
"runtime_job_id": partition.get("jobId"),
"runtime_batch_partition": partition.get("index"),
"success_probability": success,
"raw_success_probability": raw_success,
"orbit_mode": orbit_report.get("mode"),
"transpilation_mode": orbit_report.get("transpilationMode"),
"physical_layout": orbit_report.get("physicalLayout"),
"dd_status": orbit_report.get("status", "not_applied"),
"num_sequences_added": orbit_report.get("numSequencesAdded", 0),
"num_gaps_filled": orbit_report.get("numGapsFilled", 0),
"dynamic_dd_seq": orbit_report.get("dynamicDdSeq"),
"mem_status": mem_report.get("status", "not_requested"),
"warnings": orbit_report.get("warnings", [])
+ mem_report.get("warnings", []),
}
)

process_fidelity = defaultdict(dict)
raw_process_fidelity = defaultdict(dict)
mean_success_probability = defaultdict(dict)
raw_mean_success_probability = defaultdict(dict)

for (n_qubits, label), probabilities in sorted(grouped_success.items()):
n_key = str(n_qubits)
process_fidelity[n_key][label] = (
process_fidelity_from_success_probabilities(probabilities)
)
mean_success_probability[n_key][label] = float(np.mean(probabilities))

for (n_qubits, label), probabilities in sorted(grouped_raw_success.items()):
n_key = str(n_qubits)
raw_process_fidelity[n_key][label] = (
process_fidelity_from_success_probabilities(probabilities)
)
raw_mean_success_probability[n_key][label] = float(np.mean(probabilities))

process_fidelity = dict(process_fidelity)
raw_process_fidelity = dict(raw_process_fidelity)
mean_success_probability = dict(mean_success_probability)
raw_mean_success_probability = dict(raw_mean_success_probability)

{
"function_job_id": job.job_id,
"runtime_batch_id": batch_report.get("id"),
"runtime_job_ids": batch_report.get("jobIds", []),
"runtime_batch_partitions": batch_partitions,
"n_groups": N_GROUPS,
"process_fidelity": process_fidelity,
"mean_success_probability": mean_success_probability,
}
{'function_job_id': '4b953302-afc3-4889-9ecf-e63d78b4e252',
'runtime_batch_id': '096da6a9-cbea-4940-bd6f-3bdebfad436e',
'runtime_job_ids': ['dabhtfh6e67c73a1dspg',
'dabhtkp6e67c73a1dt0g',
'dabhtple36ac739ff7ag'],
'runtime_batch_partitions': [{'index': 0,
'pubStart': 0,
'pubCount': 300,
'jobId': 'dabhtfh6e67c73a1dspg'},
{'index': 1,
'pubStart': 300,
'pubCount': 300,
'jobId': 'dabhtkp6e67c73a1dt0g'},
{'index': 2,
'pubStart': 600,
'pubCount': 300,
'jobId': 'dabhtple36ac739ff7ag'}],
'n_groups': [[40, 2, 7, 15, 10], [35, 3, 6, 20, 9], [30, 4, 5, 25, 8]],
'process_fidelity': {'2': {'dynamic/orbit': 0.96795267841156,
'dynamic/raw': 0.982106196462969,
'unitary/raw': 0.9813114213091467},
'3': {'dynamic/orbit': 0.9260299657995851,
'dynamic/raw': 0.9561294394338111,
'unitary/raw': 0.9500873287318629},
'4': {'dynamic/orbit': 0.8830473125154273,
'dynamic/raw': 0.8338948172317725,
'unitary/raw': 0.8723284938083211},
'5': {'dynamic/orbit': 0.8345263398914793,
'dynamic/raw': 0.7202370952637628,
'unitary/raw': 0.7886522480980407},
'6': {'dynamic/orbit': 0.7555427381229081,
'dynamic/raw': 0.5132161092086612,
'unitary/raw': 0.7212899564100468},
'7': {'dynamic/orbit': 0.7050351887334156,
'dynamic/raw': 0.3785120988393675,
'unitary/raw': 0.6045265978953258},
'8': {'dynamic/orbit': 0.6646106162043616,
'dynamic/raw': 0.16869967802023164,
'unitary/raw': 0.5043677114922691},
'9': {'dynamic/orbit': 0.5230999959600392,
'dynamic/raw': 0.03629305030182246,
'unitary/raw': 0.4292104907804543},
'10': {'dynamic/orbit': 0.5578720776016518,
'dynamic/raw': 0.041261905412736485,
'unitary/raw': 0.3509234542669733},
'15': {'dynamic/orbit': 0.2580377878661623,
'dynamic/raw': 3.083881578947369e-05,
'unitary/raw': 0.09345722346055228},
'20': {'dynamic/orbit': 0.05567530378670723,
'dynamic/raw': 0.0,
'unitary/raw': 0.00010793585526315788},
'25': {'dynamic/orbit': 0.003482218097213064,
'dynamic/raw': 0.0,
'unitary/raw': 0.0},
'30': {'dynamic/orbit': 7.268778589499868e-06,
'dynamic/raw': 0.0,
'unitary/raw': 0.0},
'35': {'dynamic/orbit': 0.0, 'dynamic/raw': 0.0, 'unitary/raw': 0.0},
'40': {'dynamic/orbit': 0.0, 'dynamic/raw': 0.0, 'unitary/raw': 0.0}},
'mean_success_probability': {'2': {'dynamic/orbit': 0.96796875,
'dynamic/raw': 0.98212890625,
'unitary/raw': 0.98134765625},
'3': {'dynamic/orbit': 0.92607421875,
'dynamic/raw': 0.95615234375,
'unitary/raw': 0.950146484375},
'4': {'dynamic/orbit': 0.88310546875,
'dynamic/raw': 0.833935546875,
'unitary/raw': 0.872705078125},
'5': {'dynamic/orbit': 0.8345703125,
'dynamic/raw': 0.7203125,
'unitary/raw': 0.78896484375},
'6': {'dynamic/orbit': 0.7556640625,
'dynamic/raw': 0.51337890625,
'unitary/raw': 0.721826171875},
'7': {'dynamic/orbit': 0.70517578125,
'dynamic/raw': 0.3787109375,
'unitary/raw': 0.605419921875},
'8': {'dynamic/orbit': 0.66484375,
'dynamic/raw': 0.16884765625,
'unitary/raw': 0.50576171875},
'9': {'dynamic/orbit': 0.5232421875,
'dynamic/raw': 0.0365234375,
'unitary/raw': 0.43203125},
'10': {'dynamic/orbit': 0.5580078125,
'dynamic/raw': 0.04150390625,
'unitary/raw': 0.353466796875},
'15': {'dynamic/orbit': 0.258349609375,
'dynamic/raw': 0.0001953125,
'unitary/raw': 0.09453125},
'20': {'dynamic/orbit': 0.057568359375,
'dynamic/raw': 0.0,
'unitary/raw': 0.000341796875},
'25': {'dynamic/orbit': 0.006396484375,
'dynamic/raw': 0.0,
'unitary/raw': 0.0},
'30': {'dynamic/orbit': 0.000146484375,
'dynamic/raw': 0.0,
'unitary/raw': 0.0},
'35': {'dynamic/orbit': 0.0, 'dynamic/raw': 0.0, 'unitary/raw': 0.0},
'40': {'dynamic/orbit': 0.0, 'dynamic/raw': 0.0, 'unitary/raw': 0.0}}}

Suriin ang Orbit DD metadata​

Sinusuri ng buod sa ibaba ang metadata ng dynamic/orbit PUB sa halip na ipagpalagay na naipasok ang hiniling na DD. Suriin ang status, ang naiulat na dynamic DD sequence, mga babala, at ang bilang ng mga napunong gap at naidagdag na sequence. Ang matagumpay na pagpasok ay dapat magbunga ng hindi-zero na bilang para sa hindi bababa sa ilang PUB, ngunit ang eksaktong halaga ay nakadepende sa naka-schedule na circuit, mga limitasyon sa timing ng backend, at circuit size. Inilalarawan ng metadata na ito ang naaplikang sequence ng Orbit; hindi ito dapat tawaging FC-DD protocol ng papel maliban kung malinaw na itinatatag ng ulat ang pagkakatumbas na iyon.

dd_summary = defaultdict(lambda: Counter())
sequence_totals = defaultdict(int)
warning_examples = []

for summary in pub_summaries:
if summary["label"] != "dynamic/orbit":
continue
n_key = str(summary["n_qubits"])
dd_summary[n_key][summary["dd_status"]] += 1
sequence_totals[n_key] += int(summary.get("num_sequences_added") or 0)
if summary.get("warnings") and len(warning_examples) < 5:
warning_examples.append(
{
"n_qubits": summary["n_qubits"],
"target_decimal": summary["target_decimal"],
"warnings": summary["warnings"],
}
)

{
"dynamic_orbit_dd_status_counts": {
key: dict(value) for key, value in dd_summary.items()
},
"dynamic_orbit_sequences_added": dict(sequence_totals),
"warning_examples": warning_examples,
}
{'dynamic_orbit_dd_status_counts': {'40': {'dd_inserted': 20},
'2': {'dd_inserted': 20},
'7': {'dd_inserted': 20},
'15': {'dd_inserted': 20},
'10': {'dd_inserted': 20},
'35': {'dd_inserted': 20},
'3': {'dd_inserted': 20},
'6': {'dd_inserted': 20},
'20': {'dd_inserted': 20},
'9': {'dd_inserted': 20},
'30': {'dd_inserted': 20},
'4': {'dd_inserted': 20},
'5': {'dd_inserted': 20},
'25': {'dd_inserted': 20},
'8': {'dd_inserted': 20}},
'dynamic_orbit_sequences_added': {'40': 31200,
'2': 40,
'7': 840,
'15': 4200,
'10': 1800,
'35': 23800,
'3': 120,
'6': 600,
'20': 7600,
'9': 1440,
'30': 17400,
'4': 240,
'5': 400,
'25': 12000,
'8': 1120},
'warning_examples': [{'n_qubits': 40,
'target_decimal': 853235401719,
'warnings': ['Post-DD x/y pulses are rewritten into the target basis (e.g. sx) for ISA compliance; this shifts exact pulse timing within each gap, so DD spacing is approximate. A future release will emit native pulses directly.',
'MEM was applied unconditionally to the returned output bitstring without inferring whether each bit came from a terminal measurement or a mid-circuit measurement. This is meaningful for bits intended as circuit outputs, but Orbit does not retroactively or in real time change conditional branches that used unmitigated measurement results.']},
{'n_qubits': 40,
'target_decimal': 954673909846,
'warnings': ['Post-DD x/y pulses are rewritten into the target basis (e.g. sx) for ISA compliance; this shifts exact pulse timing within each gap, so DD spacing is approximate. A future release will emit native pulses directly.',
'MEM was applied unconditionally to the returned output bitstring without inferring whether each bit came from a terminal measurement or a mid-circuit measurement. This is meaningful for bits intended as circuit outputs, but Orbit does not retroactively or in real time change conditional branches that used unmitigated measurement results.']},
{'n_qubits': 40,
'target_decimal': 524641045908,
'warnings': ['Post-DD x/y pulses are rewritten into the target basis (e.g. sx) for ISA compliance; this shifts exact pulse timing within each gap, so DD spacing is approximate. A future release will emit native pulses directly.',
'MEM was applied unconditionally to the returned output bitstring without inferring whether each bit came from a terminal measurement or a mid-circuit measurement. This is meaningful for bits intended as circuit outputs, but Orbit does not retroactively or in real time change conditional branches that used unmitigated measurement results.']},
{'n_qubits': 40,
'target_decimal': 185651043478,
'warnings': ['Post-DD x/y pulses are rewritten into the target basis (e.g. sx) for ISA compliance; this shifts exact pulse timing within each gap, so DD spacing is approximate. A future release will emit native pulses directly.',
'MEM was applied unconditionally to the returned output bitstring without inferring whether each bit came from a terminal measurement or a mid-circuit measurement. This is meaningful for bits intended as circuit outputs, but Orbit does not retroactively or in real time change conditional branches that used unmitigated measurement results.']},
{'n_qubits': 40,
'target_decimal': 587114273567,
'warnings': ['Post-DD x/y pulses are rewritten into the target basis (e.g. sx) for ISA compliance; this shifts exact pulse timing within each gap, so DD spacing is approximate. A future release will emit native pulses directly.',
'MEM was applied unconditionally to the returned output bitstring without inferring whether each bit came from a terminal measurement or a mid-circuit measurement. This is meaningful for bits intended as circuit outputs, but Orbit does not retroactively or in real time change conditional branches that used unmitigated measurement results.']}]}

I-plot ang mga process-fidelity curve​

Ipinapakita ng plot ang sampled na QFT process-fidelity point estimate laban sa bilang ng qubit para sa tatlong estratehiya. Ang dynamic/raw at dynamic/orbit ay may parehong physical layout sa bawat size; ginagamit ng unitary/raw ang layout at routing ng transpiler.

Hindi tulad ng Figure 2a, hindi ipinapakita ng plot na ito ang isang unitary-with-DD curve o uncertainty band, at ang mga raw curve nito ay hindi readout-mitigated. Mas mainam itong basahin bilang isang Figure-2a-style na paghahambing ng scaling para sa Orbit workflow na ito, hindi bilang isang direktang reproduction ng mga published curve.

from datetime import datetime
from zoneinfo import ZoneInfo

finished_local = datetime.now(ZoneInfo("America/Los_Angeles"))
labels = ["dynamic/orbit", "dynamic/raw", "unitary/raw"]
colors = {
"dynamic/orbit": "#26735b",
"dynamic/raw": "#9b1c31",
"unitary/raw": "#6e6e6e",
}
pretty_labels = {
"dynamic/orbit": "Dynamic QFT with Orbit",
"dynamic/raw": "Dynamic QFT",
"unitary/raw": "Unitary QFT",
}

series = []
for label in labels:
values = [process_fidelity[str(n)][label] for n in N_VALUES]
log_values = [value if value > 0.0 else float("nan") for value in values]
series.append((label, values, log_values))

nonzero_values = [
value
for _, _, log_values in series
for value in log_values
if value > 0.0
]
if not nonzero_values:
raise RuntimeError(
"No nonzero process-fidelity values found for log inset"
)
log_floor = min(nonzero_values) / 2

fig, ax = plt.subplots(figsize=(9.8, 5.6))
for label, values, _ in series:
ax.plot(
N_VALUES,
values,
marker="o",
linewidth=2.0,
markersize=5,
color=colors[label],
label=pretty_labels[label],
)

ax.set_xlabel("N qubits")
ax.set_ylabel("Process fidelity")
finished_time_for_title = finished_local
ax.set_title(
f"Dynamic QFT Orbit results on {IBM_BACKEND_NAME}\n"
f"Job finished {finished_time_for_title:%Y-%m-%d %H:%M %Z}"
)
ax.set_xticks(N_VALUES)
ax.set_ylim(bottom=0)
ax.grid(axis="both", alpha=0.25)
ax.legend(loc="upper right")

inset = ax.inset_axes([0.53, 0.31, 0.44, 0.43])
for label, _, log_values in series:
inset.plot(
N_VALUES,
log_values,
marker="o",
linewidth=2.0,
markersize=5,
color=colors[label],
)
inset.set_yscale("log")
inset.set_ylim(bottom=log_floor)
inset.set_xlim(min(N_VALUES), max(N_VALUES))
inset.set_title("Log scale; zeros omitted", fontsize=9)
inset.grid(axis="both", alpha=0.25)
inset.tick_params(axis="both", labelsize=8)
inset.patch.set_alpha(0.96)

fig.tight_layout()
plt.show()

Output of the previous code cell

References​

  1. E. Bäumer et al., "Quantum Fourier Transform Using Dynamic Circuits," arXiv:2403.09514; Physical Review Letters 133, 150602 (2024)

  2. Introduction to Qiskit Functions

  3. Mga limitasyon ng Quantum Compute para sa stretch variables

  4. IBM Quantum error codes: 6073

  5. Run jobs in a batch

Mga susunod na hakbang​

  • Tingnan ang dokumentasyon ng Orbit guide at API reference.

  • Subukan ang ibang backend, alternatibong layout, o mag-eksperimento gamit ang alternatibong orbit-enabled na dynamical decoupling sequence sa pamamagitan ng pagbabago ng opsyong dd_strategy. Tandaan na dahil sa eksperimental na katangian ng mga dynamic circuit, kailangan mong maging maingat sa mga posibleng paraan ng pagkabigo ng job (tingnan ang [3] at [4]). Kung makakatagpo ka ng negatibong stretch value [3], subukan ang mas maliit (mas kaunting pulso) na DD sequence. Kung makakatagpo ka ng [4], dagdagan ang NUM_BATCH_PARTITIONS, bawasan ang M, o bawasan ang pinakamalaking halaga sa N_VALUES, na lahat ay tinukoy sa Setup section malapit sa itaas.