דלג לתוכן הראשי

מדדו נאמנות תהליך של QFT+M עם Orbit, פונקציית Qiskit מבית Quantum Elements

אומדן שימוש: 2 דקות על מעבד Heron r3. (הערה: זהו אומדן בלבד. זמן הריצה שלכם עשוי להשתנות.) כברירת מחדל, המדריך הזה שולח שלוש עבודות פונקציית Orbit לתוך עומס עבודה אחד במצב batch של IBM Quantum Compute Service, עם 300 PUB לכל עבודה, ובסך הכל 900 PUB ו-921,600 ירי.

אזהרה: מעגלים דינמיים הם כרגע תכונה ניסיונית, והם כפופים למגבלות ב-Quantum Compute [3] שעלולות לגרום לכשלים בעבודות. לדוגמה, שגיאה 6073 מציינת שעבודה חרגה ממגבלת הזיכרון של חומרת הבקרה הקלאסית [4]. מחברת זו מפחיתה סיכון זה על ידי חלוקת גדלי המעגלים בין שלוש עבודות Quantum Compute באצווה אחת [5]. כל השוואה בגודל קבוע נשארת בעבודה אחת, בעוד שגדלים גדולים וקטנים מזווגים כדי לאזן את עומסי הבקרה הקלאסית של העבודות.

תוצאות למידה

  • להכין את מצבי המכפלה QFTx\mathrm{QFT}^\dagger|x\rangle המשמשים את אומדן נאמנות התהליך הנדגם בתרשים 2a במקור [1].

  • לבנות יישומים שקולים יוניטריים ודינמיים של התמרת פורייה קוונטית ואחריה מדידה (QFT+M).

  • לבחור קיוביטים פיזיים למעגלים דינמיים באמצעות נתוני כיול וקישוריות עדכניים.

  • להשוות אומדני נאמנות תהליך יוניטרי גולמי, דינמי גולמי, ודינמי משופר-Orbit של QFT+M ככל שגודל המעגל גדל.

  • להשתמש בממשק ה-API הפשוט של Orbit לתרגום עם mode="raw" ו-transpilation_mode="validate".

  • להגיש מספר עומסי עבודה של Orbit דרך ה-API של מצב batch תוך שמירה על כל השוואת שלוש-אסטרטגיות בגודל קבוע באותה עבודה.

  • לבדוק את המטא-נתונים של Orbit כדי לוודא האם decoupling דינמי (DD) והפחתת שגיאת מדידה (MEM) הופעלו.

רקע

תרשים 2a במקור [1] בוחן את נאמנות התהליך של ערוץ QFT+M האידיאלי כנגד יישומים יוניטריים ודינמיים רועשים. עבור תווית xx בבסיס חישובי שנדגמה, הבנצ'מרק מכין את QFTx\mathrm{QFT}^\dagger|x\rangle, מיישם את יישום QFT+M הרועש, ואומד את ההסתברות pxp_x לקבל את הפלט האידיאלי המתאים. מצבי ה-inverse-QFT הללו הם ניתנים להפרדה וניתן להכין אותם ביעילות עם שערי Hadamard וסיבובי פאזה וירטואליים.

עבור mm תוויות שנדגמו באופן בלתי תלוי, המחברת משתמשת באומדן חסר ההטיה שנגזר במקור [1]:

F^proc=mm1(1m=1mpx)21m(m1)=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}.

הבנייה הדינמית מחליפה את שערי הפאזה המבוקרים (controlled-phase) של QFT+M היוניטרי במדידות אמצע-מעגל וסיבובי פאזה מותנים באופן קלאסי [1]. באמצעות דחיית מדידה (deferred measurement), לשני המעגלים יש אותה התפלגות פלט אידיאלית. הצורה הדינמית מסירה את הדרישה לשערי שני-קיוביטים בין כל הזוגות (all-to-all) ובמקום זאת משתמשת ב-O(n)O(n) מדידות אמצע-מעגל עם feedforward וללא אילוץ קישוריות. מדידה ו-feedforward גם משאירים תקופות סרק ארוכות בקיוביטים שטרם נמדדו, מה שהופך את ה-DD לרלוונטי במיוחד.

קשר לתרשים 2a. מחברת זו עוקבת אחר פרוטוקול נאמנות התהליך הנדגם של המאמר, אך היא עיבוד מדריך ממוקד-Orbit ולא שחזור. לדוגמה, בעוד שתרשים 2a השתמש ב-ibm_kyiv עם 2000 ירי, אנחנו משתמשים במכשיר מודרני ibm_aachen עם מספר ירי קטן יותר של 1024 כדי לשמר זמן QPU.

תוצאות לדוגמה

התרשים הסטטי למטה מציג את עקומות נאמנות התהליך הממוצעות משלוש עבודות פיתוח רצופות שהורצו על ibm_aachen בתהליך המתואר להלן. כפי שמודגם כאן, Orbit יכול להגדיל משמעותית את איכות המעגלים הדינמיים; QFT דינמי תואם לאיכות הבנצ'מרק שפורסמה ומראה שיפור לעומת QFT יוניטרי סטנדרטי. כפי שנראה, תוצאות אלה נובעות מבחירה אוטומטית טובה של קיוביטים, הכנסה אוטומטית של decoupling דינמי (לא מותאם ידנית לבעיה זו), והפחתת שגיאת מדידה. לשם הכיף, הקפידו להשוות תוצאות אלה לתוצאות שלכם בסוף, במיוחד אם תבחרו backend שונה.

הערה: תוצאות אלה הן תמונת מצב להמחשה בלבד מריצה מוקדמת ומוצלחת עם Orbit, ואינן ערובה לביצועים. התוצאות למטה אמורות להיראות דומות מבחינה איכותית, אך הפרטים תלויים במכשיר שנבחר ובתכונותיו, במיוחד שגיאות מדידה וסרק, בזמן הריצה. נאמנות תהליך QFT על 'ibm_aachen'

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-catalog qiskit-ibm-runtime

דרישות

התקינו את הגרסאות העדכניות ביותר של החבילות הבאות לפני הרצת מדריך זה:

  • numpy

  • matplotlib

  • qiskit

  • qiskit-ibm-runtime

  • qiskit-ibm-catalog

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

הגדרה

התחברו לפלטפורמת IBM Quantum®, טענו את ibm_aachen, וטענו את Quantum Elements Orbit מקטלוג פונקציות Qiskit. הסריקה כברירת מחדל מעריכה 15 גדלי מעגלים, 20 מחרוזות ביט נדגמות לכל גודל, ושלוש אסטרטגיות. NUM_BATCH_JOBS=3 מחלק את הגדלים בין שלוש עבודות באצווה אחת. הפחיתו את N_VALUES או M, או הגדילו את NUM_BATCH_JOBS, אם עבודת מעגל דינמי בודדת עדיין מגיעה למגבלת הזיכרון של הבקרה הקלאסית של ה-backend. הפחיתו את SHOTS כאשר המטרה היא להפחית שימוש בביצוע ולא את מספר או מורכבות המעגלים.

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 Batch, QiskitRuntimeService

IBM_BACKEND_NAME = "ibm_aachen"

N_VALUES = [2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40]
M = 20
SHOTS = 1024
RNG_SEED = 12345
OPTIMIZATION_LEVEL = 0
NUM_BATCH_JOBS = 3
STRATEGY_LABELS = ("unitary/raw", "dynamic/raw", "dynamic/orbit")

def balanced_n_groups(
n_values: list[int], num_jobs: int = 3
) -> list[list[int]]:
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_JOBS 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_JOBS)

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}."
)

{
"backend": backend.name,
"num_qubits": backend.num_qubits,
"n_values": N_VALUES,
"m": M,
"shots": SHOTS,
"num_function_jobs": NUM_BATCH_JOBS,
"n_groups": N_GROUPS,
"pubs_per_job": [
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,
}
qiskit_runtime_service._discover_account:WARNING:2026-07-21 15:57:39,310: Loading account with the given token. A saved account will not be used.
{'backend': 'ibm_aachen',
'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': 3,
'n_groups': [[40, 2, 7, 15, 10], [35, 3, 6, 20, 9], [30, 4, 5, 25, 8]],
'pubs_per_job': [300, 300, 300],
'total_pubs': 900,
'total_shots': 921600}

בניית מעגלי QFT+M

עבור כל מספר שלם xx נדגם, bit_inv_qft מכינה את מצב המכפלה QFTx\mathrm{QFT}^\dagger|x\rangle עם שערי Hadamard ואחריהם סיבובי פאזה. המחברת מוסיפה לאחר מכן או את QFT היוניטרי הסטנדרטי או את המקבילה הדינמית הסמי-קלאסית שלו, QFT+M.

שני היישומים משמיטים את רשת ה-swap הסופית. סדר תצוגת הביטים הקלאסיים ב-Qiskit גורם אפוא למחרוזת המדידה הצפויה להיות הפוך של הייצוג הבינארי המרופד באפסים של xx, שמקודד על ידי format(x, f"0{n}b")[::-1].

def bit_inv_qft(circuit: QuantumCircuit, x: int, conv: str = "LSB") -> None:
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:
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:
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:
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:
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 format(int(x), f"0{n_qubits}b")[::-1]

def process_fidelity_from_success_probabilities(
success_probabilities: list[float],
) -> float:
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))
)

בחירת קיוביטים פיזיים למעגל דינמי

היישום הדינמי אינו דורש שערי שני-קיוביטים, כך שהקיוביטים הפיזיים שלו אינם צריכים ליצור תת-גרף מקושר. עבור כל גודל מעגל, הבורר מדרג את קיוביטי ה-backend הנוכחיים באמצעות ציון ששוקלל ב-80% לכיוון שגיאת קריאה נמוכה יותר ו-10% כל אחד לכיוון T1T_1 ו-T2T_2 גבוהים יותר. תחילה הוא בוחר קיוביטים בעלי ציון גבוה ללא צימודים ישירים ביניהם כשאפשר, מה שיכול להפחית חשיפה להפרעות צולבות (crosstalk) בין שכנים קרובים, ולאחר מכן ממלא כל מיקום שנותר לפי ציון.

הווריאנטים dynamic/raw ו-dynamic/orbit משתמשים בדיוק באותה פריסה שנבחרה עבור גודל נתון, מה שהופך את השוואתם לפריסה-מבוקרת. מעגל unitary/raw ממופה ומנותב במקום זאת על ידי המתרגם (transpiler) מכיוון שהוא זקוק לקישוריות שני-קיוביטים. בחירה מבוססת-כיול בזמן ריצה זו ספציפית למדריך זה; היא אינה הפריסה הקבועה של 40 קיוביטים ibm_kyiv שנעשה בה שימוש בניסויי תרשים 2a של המאמר.

def value_from_property(raw):
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:
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:
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]]:
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]]:
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:
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]:
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]:
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': 0, 't1': 0.0002514242577986401, 't2': 0.00037559012475638467, 'measurement_error': 0.0028076171875, 'score': 1.0}, {'qubit': 20, 't1': 0.0002526252407383437, 't2': 0.00038493543771861573, 'measurement_error': 0.00390625, 'score': 1.0}, {'qubit': 25, 't1': 0.0002712841332005567, 't2': 0.00025793268824583597, 'measurement_error': 0.0040283203125, 'score': 1.0}]
Worst 3 qubits (according to our scoring):
[{'qubit': 146, 't1': 7.619772882181663e-05, 't2': 0.00014166983578724752, 'measurement_error': 0.0802001953125, 'score': 0.05893378230453208}, {'qubit': 51, 't1': 0.00014200221819602618, 't2': 1.930870507157441e-06, 'measurement_error': 0.054443359375, 'score': 0.04600110909801309}, {'qubit': 35, 't1': 7.116087485472031e-05, 't2': 9.463696242928626e-05, 'measurement_error': 0.14501953125, 'score': 0.03289891864200328}]

הכנת ה-PUBs לבנצ'מרק

עבור כל זוג (N, x), המחברת מתרגמת (transpiles) תחילה את המעגלים הלוגיים ויוצרת PUB אחד ל-Sampler עבור כל אסטרטגיה:

  • unitary/raw: QFT+M יוניטרי על הפריסה שנבחרה על ידי המתרגם, עם ניתוב כנדרש וללא DD או MEM של Orbit.

  • dynamic/raw: QFT+M דינמי על הקיוביטים הפיזיים שנבחרו בכיול, ללא DD או MEM של Orbit.

  • dynamic/orbit: אותו מעגל דינמי מתורגם על אותם קיוביטים פיזיים, עם DD ו-MEM של Orbit מופעלים.

ה-PUB המשופר-Orbit משתמש ב-transpilation_mode="validate" מכיוון שהמיפוי שלו כבר נבחר. Orbit מאמת את המעגל הפיזי שסופק במקום למפות אותו מחדש, ולאחר מכן מיישם את צינור ה-DD וה-MEM שלו. מכיוון שרק עקומת הדינמי המשופר מבקשת MEM, אין לפרש זאת כהשוואה מבודדת של DD-מול-ללא-DD.

ה-PUBs, האפשרויות לכל-PUB, ורשומות התוצאה מאוחסנים לפי אינדקס עבודת-האצווה. עבור כל NN קבוע, ה-PUBs unitary/raw, dynamic/raw, ו-dynamic/orbit נשמרים יחד באותה עבודה. עוזר הקיבוץ מזווג גדלי מעגל גדולים וקטנים, מחליף את הסדר שלהם, ומאזן את סכום ה-NN בין שלוש העבודות כפרוקסי פשוט לעומס העבודה של הבקרה הקלאסית.

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_by_job = [[] for _ in N_GROUPS]
pub_options_by_job = [[] for _ in N_GROUPS]
pub_records_by_job = [[] for _ in N_GROUPS]
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 job_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_by_job[job_index].append((circuit, None, SHOTS))
pub_options_by_job[job_index].append(options)
pub_records_by_job[job_index].append(
{
"job_index": job_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(),
}
)

{
"num_function_jobs": 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_job": {
job_index: len(pubs) for job_index, pubs in enumerate(pubs_by_job)
},
"expected_executions_per_job": {
job_index: len(pubs) * SHOTS
for job_index, pubs in enumerate(pubs_by_job)
},
"first_pub_record_by_job": {
job_index: records[0]
for job_index, records in enumerate(pub_records_by_job)
},
"largest_dynamic_qubit_set": layout_summary[str(max(N_VALUES))][
"dynamic_qubits"
],
}
{'num_function_jobs': 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_job': {0: 300, 1: 300, 2: 300},
'expected_executions_per_job': {0: 307200, 1: 307200, 2: 307200},
'first_pub_record_by_job': {0: {'job_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},
1: {'job_index': 1,
'n_qubits': 35,
'target_decimal': 26888951661,
'target_bitstring': '10110110111010110010110101000010011',
'label': 'unitary/raw',
'pub_options': {'mode': 'raw'},
'dynamic_qubits': None,
'transpiled_depth': 3614,
'transpiled_size': 21204},
2: {'job_index': 2,
'n_qubits': 30,
'target_decimal': 620442965,
'target_bitstring': '101010101010110011011111001001',
'label': 'unitary/raw',
'pub_options': {'mode': 'raw'},
'dynamic_qubits': None,
'transpiled_depth': 2835,
'transpiled_size': 15078}},
'largest_dynamic_qubit_set': [0,
20,
25,
27,
33,
59,
74,
80,
95,
144,
151,
155,
79,
90,
60,
68,
114,
107,
13,
126,
133,
103,
3,
87,
53,
41,
130,
5,
98,
135,
153,
15,
116,
45,
7,
48,
136,
11,
147,
77]}

הרצת הבנצ'מרק

צרו אצווה אחת, ולאחר מכן הגישו לתוכה שלוש עבודות פונקציית Orbit.

העבודות מחולקות לפי קבוצות מספר-קיוביטים כך שכל שלוש האסטרטגיות עבור NN קבוע ניתנות להשוואה. כלומר, כל 60 ה-PUBs עבור NN קבוע — 20 קלטים נדגמים כפול שלוש האסטרטגיות — מתבצעים אפוא באותה עבודה וניתן להשוותם בצורה הוגנת ככל האפשר (אחרת, אם הורצו בעבודות שונות, המכשיר יכול לסטות בזמן ההמתנה בתור). הקבוצות כברירת מחדל משלבות מעגלים גדולים וקטנים ומכילות 300 PUB כל אחת, מה שמפחית את הסיכוי שעבודה אחת תצבור את כל התוכניות הדינמיות הגדולות ביותר תוך שמירה על השוואות בתוך-העבודה.

runtime_batch = Batch(backend=backend)
jobs = []
try:
for job_index, pubs in enumerate(pubs_by_job):
jobs.append(
quantum_elements_orbit.run(
primitive="sampler",
pubs=pubs,
backend_name=backend.name,
options={
"pub_options": pub_options_by_job[job_index],
"save_backend_info": True,
},
)
)
except Exception:
runtime_batch.close()
raise

{
"runtime_batch_id": runtime_batch.session_id,
"jobs": {
job_index: {
"backend": backend.name,
"function_job_id": job.job_id,
"status": job.status(),
"n_values": N_GROUPS[job_index],
"num_pubs": len(pubs_by_job[job_index]),
}
for job_index, job in enumerate(jobs)
},
}
{'runtime_batch_id': '80120e36-436d-46c7-96c9-597ec86060c1',
'jobs': {0: {'backend': 'ibm_aachen',
'function_job_id': '2b6b05ac-0136-40f0-94bf-ade5658f5f4f',
'status': 'QUEUED',
'n_values': [40, 2, 7, 15, 10],
'num_pubs': 300},
1: {'backend': 'ibm_aachen',
'function_job_id': '4f046fd4-80e4-460b-87c7-e7252691f764',
'status': 'QUEUED',
'n_values': [35, 3, 6, 20, 9],
'num_pubs': 300},
2: {'backend': 'ibm_aachen',
'function_job_id': '6d70d64d-fa38-4ca2-9cbd-ffda5d8c99be',
'status': 'QUEUED',
'n_values': [30, 4, 5, 25, 8],
'num_pubs': 300}}}

אחזור תוצאות וחישוב נאמנות תהליך

אחזרו ואמתו כל תוצאת קבוצת-קיוביטים בנפרד, ולאחר מכן מזגו את שלושת זרמי העבודה דרך הרשומות המאונדקסות לפי עבודה. האצווה נשמרת פתוחה בזמן שכל תוצאות הפונקציה מתבקשות ונסגרת בבלוק finally לאחר שכל עבודה נוסתה. עבור כל PUB, pxp_x היא ההסתברות שמוקצית למחרוזת הביטים הצפויה. extract_counts קורא את הספירות שהוחזרו לקורא; עבור dynamic/orbit, אלה הן ספירות מותאמות-MEM כאשר ההפחתה מצליחה. extract_raw_counts גם משחזר את הספירות הבלתי-מופחתות המתאימות שנרשמו במטא-נתוני Orbit. הקוד מקבץ את 20 הערכים של pxp_x עבור כל זוג (N, label) ומיישם את האומדן שהוצג לעיל.

המילון process_fidelity המצויר משתמש אפוא בספירות גולמיות עבור unitary/raw ו-dynamic/raw, אך בספירות מותאמות-MEM עבור dynamic/orbit. המילון המקביל raw_process_fidelity שומר על חישוב בלתי-מופחת עבור כל אסטרטגיה, ושימושי בעת הפרדת השפעת ה-MEM משאר צינור ה-Orbit. MEM מתקן את היסטוגרמת הפלט המוחזרת; הוא אינו יכול לשנות באופן רטרואקטיבי תוצאת מדידת אמצע-מעגל שכבר נעשה בה שימוש על ידי feedforward בזמן אמת.

def extract_counts(pub_result) -> dict[str, int]:
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]:
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:
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)

results_by_job = {}
job_failures = []
try:
for job_index, job in enumerate(jobs):
try:
job_result = job.result()
except Exception as exc:
job_logs = getattr(job, "logs", lambda: "")()
if job_logs:
print(f"Logs for job {job_index} ({job.job_id}):\n{job_logs}")
job_failures.append(
f"job {job_index} ({job.job_id}) failed: {type(exc).__name__}: {exc}"
)
continue

expected_results = len(pub_records_by_job[job_index])
if len(job_result) != expected_results:
job_failures.append(
f"job {job_index} ({job.job_id}) returned {len(job_result)} PUB results; "
f"expected {expected_results}"
)
continue
results_by_job[job_index] = job_result
finally:
runtime_batch.close()

if job_failures:
raise RuntimeError(
"One or more batched Orbit jobs failed:\n" + "\n".join(job_failures)
)

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

for job_index, job_result in sorted(results_by_job.items()):
records = pub_records_by_job[job_index]
for record, pub_result in zip(records, job_result, strict=True):
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": jobs[job_index].job_id,
"runtime_batch_id": runtime_batch.session_id,
"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)

{
"runtime_batch_id": runtime_batch.session_id,
"function_job_ids": {
job_index: job.job_id for job_index, job in enumerate(jobs)
},
"n_groups": {
job_index: group for job_index, group in enumerate(N_GROUPS)
},
"process_fidelity": process_fidelity,
"mean_success_probability": mean_success_probability,
}
{'runtime_batch_id': '80120e36-436d-46c7-96c9-597ec86060c1',
'function_job_ids': {0: '2b6b05ac-0136-40f0-94bf-ade5658f5f4f',
1: '4f046fd4-80e4-460b-87c7-e7252691f764',
2: '6d70d64d-fa38-4ca2-9cbd-ffda5d8c99be'},
'n_groups': {0: [40, 2, 7, 15, 10],
1: [35, 3, 6, 20, 9],
2: [30, 4, 5, 25, 8]},
'process_fidelity': {'2': {'dynamic/orbit': 0.9870551835473073,
'dynamic/raw': 0.9912537998030566,
'unitary/raw': 0.9884650767434809},
'3': {'dynamic/orbit': 0.9662998634131841,
'dynamic/raw': 0.9699631603283018,
'unitary/raw': 0.9388637172865901},
'4': {'dynamic/orbit': 0.9271266520750502,
'dynamic/raw': 0.7334377020091254,
'unitary/raw': 0.9010122207121433},
'5': {'dynamic/orbit': 0.8883501513887149,
'dynamic/raw': 0.6577660260669806,
'unitary/raw': 0.7806443417987445},
'6': {'dynamic/orbit': 0.8524225652033044,
'dynamic/raw': 0.4444025126308521,
'unitary/raw': 0.7167426842521228},
'7': {'dynamic/orbit': 0.832962085697061,
'dynamic/raw': 0.2253787798698553,
'unitary/raw': 0.5746335601063436},
'8': {'dynamic/orbit': 0.7881895956180588,
'dynamic/raw': 0.16909516699831612,
'unitary/raw': 0.5408263851227074},
'9': {'dynamic/orbit': 0.7422635627368794,
'dynamic/raw': 0.0242474245097341,
'unitary/raw': 0.4855953298367578},
'10': {'dynamic/orbit': 0.7002274273149545,
'dynamic/raw': 0.033718865729016285,
'unitary/raw': 0.3607634828181049},
'15': {'dynamic/orbit': 0.4694995355699914,
'dynamic/raw': 7.70970394736842e-05,
'unitary/raw': 0.054582117352985286},
'20': {'dynamic/orbit': 0.24118032284867608,
'dynamic/raw': 4.235164736271502e-22,
'unitary/raw': 0.0},
'25': {'dynamic/orbit': 0.027122712989729438,
'dynamic/raw': 0.0,
'unitary/raw': 0.0},
'30': {'dynamic/orbit': 0.0003581886014704875,
'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.987060546875,
'dynamic/raw': 0.991259765625,
'unitary/raw': 0.9884765625},
'3': {'dynamic/orbit': 0.96630859375,
'dynamic/raw': 0.969970703125,
'unitary/raw': 0.939013671875},
'4': {'dynamic/orbit': 0.9271484375,
'dynamic/raw': 0.7337890625,
'unitary/raw': 0.901318359375},
'5': {'dynamic/orbit': 0.88837890625,
'dynamic/raw': 0.657861328125,
'unitary/raw': 0.78115234375},
'6': {'dynamic/orbit': 0.85244140625,
'dynamic/raw': 0.44453125,
'unitary/raw': 0.71728515625},
'7': {'dynamic/orbit': 0.8330078125,
'dynamic/raw': 0.22568359375,
'unitary/raw': 0.575390625},
'8': {'dynamic/orbit': 0.788232421875,
'dynamic/raw': 0.169189453125,
'unitary/raw': 0.541796875},
'9': {'dynamic/orbit': 0.742333984375,
'dynamic/raw': 0.0244140625,
'unitary/raw': 0.487353515625},
'10': {'dynamic/orbit': 0.70029296875,
'dynamic/raw': 0.033935546875,
'unitary/raw': 0.363037109375},
'15': {'dynamic/orbit': 0.4697265625,
'dynamic/raw': 0.00029296875,
'unitary/raw': 0.055615234375},
'20': {'dynamic/orbit': 0.241357421875,
'dynamic/raw': 4.8828125e-05,
'unitary/raw': 0.0},
'25': {'dynamic/orbit': 0.041015625, 'dynamic/raw': 0.0, 'unitary/raw': 0.0},
'30': {'dynamic/orbit': 0.0013671875,
'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}}}

בדיקת מטא-נתוני DD של Orbit

הסיכום למטה בודק את המטא-נתונים של PUB dynamic/orbit במקום להניח שה-DD המבוקש הוכנס. בדקו את הסטטוס, את רצף ה-DD הדינמי המדווח, אזהרות, ומספרי הפערים שמולאו והרצפים שנוספו. הכנסה מוצלחת אמורה לייצר ספירות שאינן אפס עבור לפחות כמה PUBs, אך הערכים המדויקים תלויים במעגל המתוזמן, באילוצי התזמון של ה-backend, ובגודל המעגל. מטא-נתונים אלה מתארים את הרצף שהוחל על ידי Orbit; אין לתייג אותם כפרוטוקול ה-FC-DD של המאמר אלא אם הדוח קובע במפורש שקילות זו.

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.']}]}

ציור עקומות נאמנות-תהליך

התרשים מציג את אומדן הנקודה של נאמנות התהליך הנדגמת של QFT+M כפונקציה של מספר הקיוביטים עבור שלוש האסטרטגיות. dynamic/raw ו-dynamic/orbit חולקים פריסה פיזית בכל גודל; unitary/raw משתמש בפריסה ובניתוב של המתרגם.

בניגוד לתרשים 2a, תרשים זה אינו מציג עקומת יוניטרי-עם-DD או רצועות אי-ודאות, ועקומותיו הגולמיות אינן מופחתות-קריאה. הכי טוב לקרוא אותו כהשוואת שינוי קנה מידה בסגנון תרשים-2a עבור זרימת עבודת Orbit זו, לא כשחזור ישיר של העקומות שפורסמו.

from datetime import datetime
from zoneinfo import ZoneInfo

closed_at = runtime_batch.details()["closed_at"] # "2026-07-22T00:08:54.89Z"
closed_dt = datetime.fromisoformat(closed_at.replace("Z", "+00:00"))
closed_local = closed_dt.astimezone(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+M with Orbit",
"dynamic/raw": "Dynamic QFT+M",
"unitary/raw": "Unitary QFT+M",
}

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 = globals().get("finished_local", closed_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

מקורות

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

  2. מבוא לפונקציות Qiskit

  3. מגבלות Quantum Compute עבור משתני stretch

  4. קודי שגיאה של IBM Quantum: 6073

  5. הרצת עבודות באצווה

השלבים הבאים

  • עיינו בתיעוד מדריך Orbit והפניית API.

  • נסו backend שונה, פריסה חלופית, או התנסו ברצף decoupling דינמי חלופי המופעל על ידי orbit על ידי שינוי האפשרות dd_strategy. זכרו שבשל האופי הניסיוני של מעגלים דינמיים, עליכם להיזהר ממצבי כשל אפשריים בעבודה (ראו [3] ו-[4]). אם תיתקלו בערכי stretch שליליים [3], נסו רצף DD קטן יותר (עם פחות פולסים). אם תיתקלו ב-[4], הגדילו את NUM_BATCH_JOBS, הפחיתו את M, או הפחיתו את הערכים הגדולים ביותר ב-N_VALUES.