פתרון תזמון דחוי באמצעות stretch
מפרט שפת OpenQASM 3 מכיל סוג stretch שבאמצעותו ניתן לציין תזמון יחסי של פעולות במקום תזמון מוחלט. תמיכה ב-stretch כמשכי זמן עבור הוראות Delay נוספה ב-Qiskit v2.0.0. הערך הקונקרטי של משך ה-stretch נפתר בזמן קומפילציה, לאחר שמשכי ה-Gate המכויל המדויקים ידועים. המקמפל מנסה למזער את משך ה-stretch, בכפוף לאילוצי תזמון על Qubit אחד או יותר. כך ניתן לבטא עיצובי Gate כגון ריווח שווה בין Gate-ים (למשל, לממש רצף echo decoupling ממעלה גבוהה יותר), יישור שמאלי של רצף Gate-ים, או הפעלת Gate למשך תת-מעגל כלשהו — מבלי לדעת את התזמון המדויק.
דוגמאות
Dynamical decoupling
שימוש נפוץ ב-stretch הוא להפעיל dynamical decoupling על Qubit שיושב בסרק, בזמן שQubit אחר עובר פעולות מותנות.
לדוגמה, ניתן להשתמש ב-stretch כדי להפעיל רצף XX dynamical decoupling על Qubit 1, למשך הבלוק המותנה המופעל על Qubit 0, כפי שמוצג בתרשים הבא:
המעגל המתאים ייראה כך. שים לב שדרושה זוג barriers כדי להגדיר את גבולות התזמון היחסי הזה.
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits
# Add barriers to define the boundaries
circuit.barrier()
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q0)
with else_:
circuit.x(q0)
# Apply an XX DD sequence with stretch on qubit 1
s = circuit.add_stretch("s")
circuit.delay(s, q1)
circuit.x(q1)
circuit.delay(expr.mul(s, 2), q1)
circuit.x(q1)
circuit.delay(s, q1)
circuit.barrier()
יישור תזמון
דוגמה זו משתמשת ב-stretch כדי להבטיח שרצף של Gate-ים בין שני barriers יהיה מיושר שמאלה, ללא קשר למשכיהם בפועל:
from qiskit import QuantumCircuit
from numpy import pi
qc = QuantumCircuit(5)
qc.barrier()
qc.cx(0, 1)
qc.u(pi/4, 0, pi/2, 2)
qc.cx(3, 4)
a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")
# Use the stretches as Delay duration.
qc.delay(a, [0, 1])
qc.delay(b, 2)
qc.delay(c, [3, 4])
qc.barrier()
כשמשתמשים ב-stretch עם Qiskit Runtime, כל שארית הנובעת מפתרון stretch מתווספת ל-delay הראשון שמשתמש ב-stretch.
דוגמה:
a = circuit.add_stretch("a")
circuit.barrier(q0, q1)
circuit.delay(100, q0)
circuit.delay(a, q1) # resolve to 26
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.barrier(q0, q1)
הקוד לעיל מתפרש לערך 25 עם שארית של 1. ה-delay הראשון [a] יקבל את השארית המוספת.
משוואת פתרון Stretch:
צפייה בערכי stretch ב-Qiskit Runtime
הערך בפועל של משך stretch נפתר בזמן קומפילציה, לאחר שהמעגל מתוזמן. כשמריצים עבודת Sampler ב-Qiskit Runtime, ניתן לצפות בערכי ה-stretch שנפתרו במטא-נתוני תוצאת העבודה. תמיכה ב-stretch ב-Qiskit Runtime היא כרגע ניסיונית, לכן יש להגדיר תחילה אפשרות ניסיונית להפעלת האחזור שלה, ואז לגשת לנתונים ישירות מהמטא-נתונים כך:
# Enable stretch value retrieval.
sampler.options.experimental = {
"execution": {
"stretch_values": True,
"scheduler_timing": True,
},
}
# Access the stretch values from the metadata.
job_result = job.result()
circuit_stretch_values = job_result[0].metadata["compilation"]["stretch_values"]
# Visualize the timing.
# Use the sliders at the bottom, the controls at the top, and the legend on the side
# of the output to customize the view.
draw_circuit_schedule_timing(ob.result()[0].metadata['compilation']['scheduler_timing']['timing'])
למרות שזמן המעגל הכולל מוחזר במטא-נתוני "compilation", זה אינו הזמן המשמש לחיוב (זמן קוונטי).
הבנת פלט המטא-נתונים
המטא-נתונים stretch_values מחזירים את המידע הבא:
- ש ם: שם ה-stretch שהוחל.
- ערך: ערך המטרה המבוקש.
- שארית: השארית מפתרון ה-stretch, שמתווספת ל-delay הראשון המשתמש ב-stretch.
- ערכים מורחבים: קבוצות ערכים המציינות את תחילת ה-stretch ואת משכו.
דוגמה
# Define the circuit
circuit = QuantumCircuit(4)
foo = circuit.add_stretch("foo")
bar = circuit.add_stretch("bar")
circuit.barrier()
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.delay(foo, 2)
circuit.x(2)
# 3*foo
circuit.delay(expr.mul(3, foo), 2)
circuit.x(2)
# 2*foo
circuit.delay(expr.mul(2, foo), 2)
circuit.delay(bar, 3)
circuit.x(3)
circuit.delay(bar, 3)
circuit.measure_all()
פלט המטא-נתונים
[{'name': 'bar',
'value': 29,
'remainder': 1,
'expanded_values': [[1365, 30], [1404, 29]]},
{'name': 'foo',
'value': 8,
'remainder': 2,
'expanded_values': [[1365, 10], [1384, 24], [1417, 16]]}
]
הערכים המוחזרים עבור משך התלויים בערך המטרה ובשארית המחושבת. לדוגמה, אלה המשכים המוחזרים עבור foo:
foo value+remainder(8+2 = 10)foo value* 3 (8 x 3 = 24)foo value* 2 (8 x 2 = 16)
ניתן להשתמש בויזואליזציה כדי לעזור להבין ולאמת את התזמון.
draw_circuit_schedule_timing(job.result()[0].metadata['compilation']['scheduler_timing']['timing'])
בתמונה הבאה, על בסיס פלט הדוגמה, foo מתאים ל-stretch-ים על Qubit 2. ה-stretch delay הראשון שמשתמש ב-foo מתחיל בסוף ה-init_play (1365). משך ה-stretch הוא 10, כך שה-delay מסתיים כאשר שער ה-x מתחיל (1365+10=1375). ניתן לפרש את ה-stretch השני והשלישי באופן דומה.

השתמש בגוררים שבתחתית, בפקדים שבראש (רחף מעל תמונת הפלט לחשיפתם), ובמקרא שבצד הפלט כדי להתאים אישית את התצוגה. רחף מעל התמונה לצפייה בנתונים מדויקים.
לפרטים מלאים, ראה את הנושא ויזואליזציה של תזמון מעגל.
מגבלות Qiskit Runtime
תמיכה ב-stretch ב-Qiskit Runtime היא כרגע ניסיונית ויש לה את האילוצים הבאים:
-
לכל היותר משתנה stretch אחד לכל קבוצת Qubit בין barriers (משתמע ומפורש). קבוצת Qubit היא Qubit אחד או יותר; קבוצות אלה חייבות להיות בלתי חופפות.
- לא תקין
- תקין
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(a, (q0, q1))
circuit.delay(b, q0) # Invalid because 2 stretches are applied on q0a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(a, (q0, q1))
circuit.delay(b, q2) -
האזור המוקף על ידי קבוצת barriers נקרא אזור barrier. לא ניתן להשתמש במשתנה stretch במספר אזורי barrier.
- לא תקין
- תקין
# Stretch a is used in two barrier regions
a = circuit.add_stretch("a")
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier((q0, q1))
# Stretch a is used inside a barrier region that is on q0 and q1
a = circuit.add_stretch("a")
circuit.barrier((q0, q1))
circuit.delay(a, q0)
circuit.barrier(q2)
circuit.delay(a, q0)
circuit.barrier((q0, q1))
-
ביטויי stretch מוגבלים לצורה
X*stretch + YכאשרXו-Yהם קבועים מסוג נקודה צפה או שלם.- לא תקין
- תקין
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
c = circuit.add_stretch("c")
# (a / b) * c is not supported
circuit.delay(expr.mul(expr.div(a, b), c), q1)from qiskit.circuit import Duration
a = circuit.add_stretch("a")
circuit.delay(expr.add(expr.mul(a, 2), Duration.dt(3)), 0) -
ביטויי stretch יכולים לכלול רק משתנה stretch יחיד.
- לא תקין
- תקין
a = circuit.add_stretch("a")
b = circuit.add_stretch("b")
circuit.delay(expr.add(a, b), 0)a = circuit.add_stretch("a")
circuit.delay(expr.add(a, a), 0) -
ביטויי stretch לא יכולים להתפתח לערכי delay שליליים. הפותר הנוכחי אינו מסיק אילוצי אי-שליליות.
- לא תקין
- תקין
from qiskit.circuit import Duration
circuit.barrier((q0, q1))
circuit.delay(20, q1)
# The length of this barrier region is 20dt, meaning the
# equation for solving stretch 'a' is a + 40dt = 20dt, giving a = -20dt.
circuit.delay(expr.add(a, Duration.dt(40)), q0)
circuit.barrier((q0, q1))circuit.barrier((q0, q1))
circuit.delay(20, q1)
circuit.delay(a, q0)
circuit.barrier((q0, q1))