انتقل إلى المحتوى الرئيسي

تأجيل حل التوقيت باستخدام stretch

تحتوي مواصفة لغة OpenQASM 3 على نوع stretch يتيح لك تحديد التوقيت النسبي للعمليات بدلاً من التوقيت المطلق. أُضيف دعم stretch كمدد للتعليمة Delay في Qiskit v2.0.0. يُحسم الحجم الفعلي لمدة stretch في وقت الترجمة، بعد معرفة المدة الدقيقة للبوابات المعايَرة. يسعى المترجم إلى تقليل مدة stretch إلى الحد الأدنى، مع مراعاة قيود التوقيت على qubit واحد أو أكثر. يمكنك بذلك التعبير عن تصميمات البوابات كتباعد البوابات بالتساوي (مثلاً لتنفيذ تسلسل فك ارتباط صدى عالي الرتبة)، أو محاذاة تسلسل بوابات إلى اليسار، أو تطبيق بوابة طوال مدة دائرة فرعية معينة، وذلك دون معرفة التوقيت الدقيق.

أمثلة

الفصل الديناميكي

من أكثر حالات استخدام stretch شيوعاً تطبيق الفصل الديناميكي على qubit في حالة خمول بينما يخضع qubit آخر لعمليات شرطية.

على سبيل المثال، يمكننا استخدام stretch لتطبيق تسلسل فصل ديناميكي XX على qubit 1، طوال مدة الكتلة الشرطية المطبقة على qubit 0، كما يوضح الرسم التالي:

صورة توضيحية للدائرة التالية

ستبدو الدائرة المقابلة كما يلي. لاحظ أن زوجاً من الحواجز ضروري لتحديد حدود هذا التوقيت النسبي.

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 لضمان محاذاة تسلسل البوابات بين حاجزين إلى اليسار، بصرف النظر عن مددها الفعلية:

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 إلى أول تأخير يستخدم 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. سيُضاف الباقي إلى أول تأخير[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، والذي يُضاف إلى أول تأخير يستخدم 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 التمددات على Qubit 2. يبدأ أول تأخير تمدد يستخدم foo عند نهاية init_play (1365). مدة التمدد هي 10، لذا ينتهي هذا التأخير عندما تبدأ بوابة x (1365+10=1375). يمكنك تفسير التمددين الثاني والثالث بالمثل.

يظهر الناتج من أمر draw_circuit_schedule_timing.

استخدم أشرطة التمرير في الأسفل، وعناصر التحكم في الأعلى (مرر المؤشر فوق صورة الناتج للكشف عنها)، والتسمية التوضيحية على جانب الناتج لتخصيص العرض. مرر المؤشر فوق الصورة لعرض البيانات الدقيقة.

للاطلاع على التفاصيل الكاملة، راجع موضوع تصور توقيت الدائرة.

قيود Qiskit Runtime

دعم stretch في Qiskit Runtime تجريبي حالياً وله القيود التالية:

  • متغير stretch واحد على الأكثر لكل مجموعة qubits بين الحواجز (الضمنية والصريحة). مجموعة الـ qubits هي 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
  • المنطقة المحاطة بمجموعة من الحواجز تُسمى منطقة الحاجز. لا يمكن استخدام متغير stretch في مناطق حاجز متعددة.

    # 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 إلى قيم تأخير سالبة. لا يستنتج المحلل الحالي قيود عدم السلبية.

    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))