การแก้ไข timing แบบ deferred โดยใช้ stretch
ข้อกำหนดภาษา OpenQASM 3 มีชนิด stretch ซึ่งช่วยให้คุณระบุ timing สัมพัทธ์ของ operation แทนที่จะเป็น timing แบบสัมบูรณ์ การรองรับ stretch เป็น duration สำหรับคำสั่ง Delay ถูกเพิ่มใน Qiskit v2.0.0 ค่าจริงของ stretch duration จะถูกแก้ไขในเวลา compile หลังจากทราบ duration ที่แน่นอนของ calibrated gate แล้ว Compiler จะพยายามลด stretch duration ให้น้อยที่สุด ภายใต้ข้อจำกัด timing บน Qubit หนึ่งตัวหรือมากกว่า จากนั้นคุณสามารถออกแบบ gate ได้ เช่น การจัด gate ให้ห่างกันอย่างสม่ำเสมอ (เพื่อใช้ echo decoupling sequence อันดับสูง), การจัด gate ให้ชิดซ้าย หรือการใช้ gate ตลอด duration ของ sub-circuit บางตัว โดยไม่จำเป็นต้องรู้ timing ที่แน่นอน
ตัวอย่าง
Dynamical decoupling
กรณีการใช้งาน stretch ที่พบบ่อยคือการใช้ dynamical decoupling กับ Qubit ที่กำลัง idle ขณะที่ Qubit อื่นกำลังดำเนินการ conditional operation
ตัวอย่างเช่น เราสามารถใช้ stretch เพื่อใช้ XX dynamical decoupling sequence กับ Qubit 1 ตลอด duration ของ conditional block ที่ใช้กับ Qubit 0 ดังที่แสดงในภาพต่อไปนี้:
Circuit ที่สอดคล้องกันจะมีลักษณะดังนี้ สังเกตว่าต้องใช้คู่ barriers เพื่อกำหนดขอบเขตของ timing สัมพัทธ์นี้
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()
การจัด scheduling ให้ตรงกัน
ตัวอย่างนี้ใช้ stretch เพื่อให้แน่ใจว่าลำดับ gate ระหว่าง barriers สองตัวถูกจัดชิดซ้าย ไม่ว่า duration จริงของมันจะเป็นเท่าไร:
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 duration จะถูกแก้ไขในเวลา compile หลังจาก Circuit ถูก schedule แล้ว เมื่อรัน Sampler job ใน Qiskit Runtime คุณสามารถดูค่า stretch ที่แก้ไขแล้วได้จาก metadata ของผลลัพธ์ job การรองรับ stretch ใน Qiskit Runtime ยังอยู่ในช่วงทดลอง ดังนั้นคุณต้องตั้งค่า option ทดลองก่อนเพื่อเปิดใช้งานการดึงข้อมูล แล้วจึงเข้าถึงข้อมูลโดยตรงจาก metadata ดังนี้:
# 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'])
แม้ว่าเวลาทั้งหมดของ Circuit จะถูกส่งคืนใน metadata "compilation" แต่นี่ไม่ใช่เวลาที่ใช้สำหรับการเรียกเก็บเงิน (QPU time)
ทำความเข้าใจผลลัพธ์ metadata
metadata stretch_values ส่งคืนข้อมูลต่อไปนี้:
- Name: ชื่อของ stretch ที่ใช้
- Value: ค่า goal ที่ต้องการ
- Remainder: เศษที่เหลือจากการแก้ไข stretch ซึ่งถูกเพิ่มให้กับ delay ตัวแรกที่ใช้ stretch นั้น
- Expanded values: ชุดค่าที่ระบุจุดเริ่มต้นและ duration ของ 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()
ผลลัพธ์ metadata
[{'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]]}
]
ค่า duration ที่ส่งคืนขึ้นอยู่กับ goal value และ remainder ที่คำนวณได้ ตัวอย่างเช่น นี่คือ duration ที่ส่งคืนสำหรับ foo:
foo value+remainder(8+2 = 10)foo value* 3 (8 x 3 = 24)foo value* 2 (8 x 2 = 16)
คุณสามารถใช้ visualization เพื่อช่วยทำความเข้าใจและตรวจสอบ timing ได้
draw_circuit_schedule_timing(job.result()[0].metadata
['compilation']['scheduler_timing']['timing'])
ในภาพต่อไปนี้ ซึ่งอ้างอิงจากผลลัพธ์ตัวอย่าง foo สอดคล้องกับ stretch บน Qubit 2 stretch delay ตัวแรกที่ใช้ foo เริ่มต้นที่ท้ายของ init_play (1365) stretch duration เป็น 10 ดังนั้น delay นั้นสิ้นสุดเมื่อ x gate เริ่มต้น (1365+10=1375) คุณสามารถตีความ stretch ที่สองและสามในลักษณะเดียวกัน

ใช้ตัวเลื่อนที่ด้านล่าง, ปุ่มควบคุมที่ด้านบน (hover เหนือภาพ output เพื่อแสดง) และ legend ที่ด้านข้างของ output เพื่อปรับแต่งการแสดงผล Hover เหนือภาพเพื่อดูข้อมูลที่แน่นอน
สำหรับรายละเอียดครบถ้วน ดูหัวข้อ Visualize circuit timing
ข้อจำกัดของ Qiskit Runtime
การรองรับ stretch ใน Qiskit Runtime ยังอยู่ในช่วงทดลองและมีข้อจำกัดต่อไปนี้:
-
มี stretch variable ได้มากที่สุดหนึ่งตัวต่อ qubit set ระหว่าง barrier (แบบ implicit และ explicit) qubit set คือ Qubit หนึ่งตัวหรือมากกว่า ซึ่ง set เหล่านี้ต้องไม่ซ้อนทับกัน
- ไม่ถูกต้อง
- ถูกต้อง
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) -
พื้นที่ที่ล้อมรอบด้วยชุด barrier เรียกว่า barrier region stretch variable ไม่สามารถใช้ใน barrier region หลายอัน
- ไม่ถูกต้อง
- ถูกต้อง
# 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 expression จำกัดให้อยู่ในรูปแบบ
X*stretch + Yโดยที่XและYเป็นค่าคงที่แบบ floating point หรือจำนวนเต็ม- ไม่ถูกต้อง
- ถูกต้อง
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 expression สามารถมี stretch variable ได้เพียงตัวเดียว
- ไม่ถูกต้อง
- ถูกต้อง
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 expression ไม่สามารถแก้ไขให้เป็นค่า delay ติดลบได้ solver ปัจจุบันไม่อนุมาน non-negativity constraint
- ไม่ถูกต้อง
- ถูกต้อง
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))