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

פתרון תזמון דחוי באמצעות 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: a+8+a+8+a+8=100=3a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24

צפייה בערכי 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 השני והשלישי באופן דומה.

הפלט מפקודת draw_circuit_schedule_timing מוצג.

השתמש בגוררים שבתחתית, בפקדים שבראש (רחף מעל תמונת הפלט לחשיפתם), ובמקרא שבצד הפלט כדי להתאים אישית את התצוגה. רחף מעל התמונה לצפייה בנתונים מדויקים.

לפרטים מלאים, ראה את הנושא ויזואליזציה של תזמון מעגל.

מגבלות 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 q0
  • האזור המוקף על ידי קבוצת 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 מוגבלים לצורה 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)
  • ביטויי stretch יכולים לכלול רק משתנה stretch יחיד.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 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))