פתרון תזמון דחוי באמצעות 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 עם IBM Quantum Compute Service, כל שארית הנובעת מפתרון 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 ב-IBM Quantum Compute Service
הערך בפועל של משך stretch נפתר בזמן קומפילציה, לאחר שהמעגל מתוזמן. כשמריצים עבודת Sampler ב-IBM Quantum Compute Service, ניתן לצפות בערכי ה-stretch שנפתרו במטא-נתוני תוצאת העבודה. תמיכה ב-stretch ב-Quantum Compute היא כרגע ניסיונית, לכן יש להגדיר תחילה אפשרות ניסיונית להפעלת האחזור שלה, ואז לגשת לנתונים ישירות מהמטא-נתונים כך:
# 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", זה אינו הזמן המשמש לחיוב (זמן QPU).
הבנת פלט המטא-נתונים
המטא-נתונים 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 השני והשלישי באופן דומה.

השתמש במחוונים שבתחתית, בפקדים שבראש (רחף מעל תמונת הפלט לחשיפתם), ובמקרא שבצד הפלט כדי להתאים אישית את התצוגה. רחף מעל התמונה לצפייה בנתונים מדויקים.
לפרטים מלאים, ראה את הנושא ויזואליזציה של תזמון מעגל.
מגבלות Quantum Compute
תמיכה ב-stretch ב-Quantum Compute היא כרגע ניסיונית ויש לה את האילוצים הבאים:
-
לכל היותר משתנה 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 regionsa = 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 q1a = 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 supportedcircuit.delay(expr.mul(expr.div(a, b), c), q1)from qiskit.circuit import Durationa = 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 Durationcircuit.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))