ข้ามไปยังเนื้อหาหลัก

การแก้ไข 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 ดังที่แสดงในภาพต่อไปนี้:

Image illustrating the following circuit

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: a+8+a+8+a+8=100=3a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24

ดูค่า 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" แต่นี่ไม่ใช่เวลาที่ใช้สำหรับการเรียกเก็บเงิน (quantum 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 ที่สองและสามในลักษณะเดียวกัน

The output from the draw_circuit_schedule_timing command is shown.

ใช้ตัวเลื่อนที่ด้านล่าง, ปุ่มควบคุมที่ด้านบน (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 q0
  • พื้นที่ที่ล้อมรอบด้วยชุด barrier เรียกว่า barrier region stretch variable ไม่สามารถใช้ใน barrier region หลายอัน

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

    Illustration of the previous code output

  • 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 supported
    circuit.delay(expr.mul(expr.div(a, b), c), q1)
  • Stretch expression สามารถมี stretch variable ได้เพียงตัวเดียว

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 0)
  • Stretch expression ไม่สามารถแก้ไขให้เป็นค่า delay ติดลบได้ solver ปัจจุบันไม่อนุมาน non-negativity constraint

    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))
Source: IBM Quantum docs — updated 25 มี.ค. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569