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

Classical feedforward และ control flow

เวอร์ชันของแพ็กเกจ

โค้ดในหน้านี้พัฒนาโดยใช้ข้อกำหนดดังต่อไปนี้ แนะนำให้ใช้เวอร์ชันเหล่านี้หรือใหม่กว่า

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Dynamic circuits พร้อมใช้งานบน Backend ทุกตัวแล้ว

Dynamic circuits เวอร์ชันใหม่พร้อมให้บริการแก่ผู้ใช้ทุกคนบน Backend ทุกตัวแล้ว ตอนนี้สามารถรัน dynamic circuits ในระดับ utility scale ได้ ดู ประกาศ สำหรับรายละเอียดเพิ่มเติม

Dynamic circuits เป็นเครื่องมือที่ทรงพลัง ซึ่งช่วยให้วัดค่า Qubit ได้ในระหว่างการรัน quantum circuit และจากนั้นดำเนินการ classical logic ภายใน Circuit โดยอิงจากผลของการวัดกลางวงจร (mid-circuit measurements) กระบวนการนี้เรียกอีกอย่างว่า classical feedforward แม้ยังเป็นช่วงต้นของการเข้าใจวิธีใช้ประโยชน์จาก dynamic circuits ได้ดีที่สุด แต่ชุมชนวิจัยควอนตัมก็ระบุกรณีการใช้งานหลายอย่างไว้แล้ว เช่น:

อย่างไรก็ตาม การปรับปรุงที่ dynamic circuits นำมานั้นมีข้อแลกเปลี่ยน การวัดกลางวงจรและ classical operations โดยทั่วไปใช้เวลารันนานกว่า two-qubit gates และเวลาที่เพิ่มขึ้นนี้อาจทำให้ประโยชน์ของการลด circuit depth หมดไป ดังนั้น การลดระยะเวลาการวัดกลางวงจรจึงเป็นจุดโฟกัสของการปรับปรุงขณะที่ IBM Quantum® เปิดตัว เวอร์ชันใหม่ ของ dynamic circuits

ข้อกำหนด OpenQASM 3 กำหนดโครงสร้าง control-flow หลายแบบ แต่ Qiskit Runtime รองรับเฉพาะคำสั่งเงื่อนไข if เท่านั้น ใน Qiskit SDK จะสอดคล้องกับเมธอด if_test บน QuantumCircuit เมธอดนี้คืนค่า context manager และมักใช้ในคำสั่ง with คู่มือนี้อธิบายวิธีใช้คำสั่งเงื่อนไขดังกล่าว

หมายเหตุ

ตัวอย่างโค้ดในคู่มือนี้ใช้คำสั่ง measure มาตรฐานสำหรับ mid-circuit measurements อย่างไรก็ตาม แนะนำให้ใช้คำสั่ง MidCircuitMeasure แทน หาก Backend รองรับ ดูรายละเอียดที่ เอกสาร Mid-circuit measurements

คำสั่ง if

คำสั่ง if ใช้เพื่อดำเนินการตามเงื่อนไขโดยอิงจากค่าของ classical bit หรือ register

ในตัวอย่างด้านล่าง เราจะใส่ Hadamard Gate ให้กับ Qubit แล้ววัดค่า ถ้าผลลัพธ์เป็น 1 เราจะใส่ X Gate บน Qubit ซึ่งมีผลพลิกกลับไปที่สถานะ 0 จากนั้นเราวัด Qubit อีกครั้ง ผลการวัดสุดท้ายควรเป็น 0 ด้วยความน่าจะเป็น 100%

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

circuit.h(q0)
# Use MidCircuitMeasure() if it's supported by the backend.
# circuit.append(MidCircuitMeasure(), [q0], [c0])
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

Output of the previous code cell

คำสั่ง with สามารถกำหนด assignment target ซึ่งเป็น context manager ที่สามารถเก็บไว้และนำไปใช้สร้าง else block ได้ โดย else block จะถูกรันเมื่อเนื้อหาใน if block ไม่ถูกรัน

ในตัวอย่างด้านล่าง เราจะกำหนด register ที่มี Qubit สองตัวและ classical bit สองบิต เราใส่ Hadamard Gate ให้กับ Qubit ตัวแรกแล้ววัดค่า ถ้าผลลัพธ์เป็น 1 เราจะใส่ Hadamard Gate บน Qubit ตัวที่สอง มิเช่นนั้น เราจะใส่ X Gate บน Qubit ตัวที่สอง สุดท้ายเราวัด Qubit ตัวที่สองด้วย

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

Output of the previous code cell

นอกจากการกำหนดเงื่อนไขบน classical bit เดียวแล้ว ยังสามารถกำหนดเงื่อนไขบนค่าของ classical register ที่ประกอบด้วยหลายบิตได้

ในตัวอย่างด้านล่าง เราใส่ Hadamard Gate ให้กับ Qubit สองตัวแล้ววัดค่า ถ้าผลลัพธ์เป็น 01 หมายถึง Qubit ตัวแรกเป็น 1 และ Qubit ตัวที่สองเป็น 0 เราจะใส่ X Gate ให้กับ Qubit ตัวที่สาม สุดท้ายเราวัด Qubit ตัวที่สาม โปรดสังเกตว่าเพื่อความชัดเจน เราเลือกระบุสถานะของ classical bit ตัวที่สาม ซึ่งเป็น 0 ไว้ในเงื่อนไข if ด้วย ในการวาด Circuit เงื่อนไขแสดงด้วยวงกลมบน classical bit ที่ถูกกำหนดเงื่อนไข วงกลมสีดำหมายถึงเงื่อนไขบน 1 ส่วนวงกลมสีขาวหมายถึงเงื่อนไขบน 0

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

Output of the previous code cell

Classical expressions

โมดูล classical expression ของ Qiskit qiskit.circuit.classical ประกอบด้วยการแสดงผลเชิงทดลองของการดำเนินการ runtime บนค่า classical ระหว่างการรัน Circuit เนื่องจากข้อจำกัดของฮาร์ดแวร์ ปัจจุบันรองรับเฉพาะเงื่อนไขของ QuantumCircuit.if_test() เท่านั้น

ตัวอย่างต่อไปนี้แสดงว่าสามารถใช้การคำนวณ parity เพื่อสร้าง n-qubit GHZ state โดยใช้ dynamic circuits ได้ อันดับแรก สร้าง n/2n/2 Bell pairs บน Qubit ที่อยู่ติดกัน จากนั้น เชื่อมต่อคู่เหล่านี้โดยใช้ layer ของ CNOT gates ระหว่างคู่ จากนั้นวัด target Qubit ของ CNOT gates ทั้งหมดก่อนหน้าและ reset แต่ละ Qubit ที่วัดแล้วให้กลับเป็นสถานะ 0\vert 0 \rangle ใส่ XX ให้กับทุก site ที่ไม่ถูกวัดซึ่ง parity ของบิตก่อนหน้าทั้งหมดเป็นเลขคี่ สุดท้าย ใส่ CNOT gates ให้กับ Qubit ที่ถูกวัดเพื่อสร้าง entanglement ขึ้นใหม่ที่สูญเสียไปจากการวัด

ในการคำนวณ parity องค์ประกอบแรกของ expression ที่สร้างขึ้นเกี่ยวข้องกับการยก Python object mr[0] ขึ้นเป็น node Value (lift ใช้เพื่อแปลง arbitrary objects ให้เป็น classical expressions) ไม่จำเป็นสำหรับ mr[1] และ classical register ที่ตามมา เนื่องจากเป็น inputs ให้กับ expr.bit_xor และการยกที่จำเป็นจะเกิดขึ้นโดยอัตโนมัติในกรณีเหล่านี้ expressions ดังกล่าวสามารถสร้างขึ้นใน loops และโครงสร้างอื่นได้

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue

# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

Output of the previous code cell

ค้นหา Backend ที่รองรับ dynamic circuits

หากต้องการค้นหา Backend ทั้งหมดที่บัญชีของคุณเข้าถึงได้และรองรับ dynamic circuits ให้รันโค้ดดังต่อไปนี้ ตัวอย่างนี้สมมติว่าคุณ บันทึก login credentials ไว้แล้ว นอกจากนี้ยังสามารถ ระบุ credentials อย่างชัดเจน เมื่อเริ่มต้น Qiskit Runtime service account ซึ่งช่วยให้ดู Backend ที่มีให้บริการบน instance หรือประเภทแผนที่เฉพาะเจาะจงได้

หมายเหตุ
  • Backend ที่บัญชีเข้าถึงได้ขึ้นอยู่กับ instance ที่ระบุใน credentials
  • Dynamic circuits เวอร์ชันใหม่พร้อมให้บริการแก่ผู้ใช้ทุกคนบน Backend ทุกตัวแล้ว ดู ประกาศ สำหรับรายละเอียดเพิ่มเติม
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_miami')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_torino')>, <IBMBackend('ibm_kingston')>]

ข้อจำกัดของ Qiskit Runtime

โปรดทราบข้อจำกัดต่อไปนี้เมื่อรัน dynamic circuits ใน Qiskit Runtime

  • เนื่องจากหน่วยความจำกายภาพที่จำกัดบน control electronics จึงมีข้อจำกัดด้านจำนวนคำสั่ง if และขนาดของ operands ด้วย ข้อจำกัดนี้เป็นฟังก์ชันของจำนวน broadcasts และจำนวน broadcasted bits ใน job (ไม่ใช่ Circuit)

    เมื่อประมวลผลเงื่อนไข if ข้อมูลการวัดต้องถูกถ่ายโอนไปยัง control logic เพื่อการประเมินนั้น broadcast คือการถ่ายโอนข้อมูล classical ที่ไม่ซ้ำกัน และ broadcasted bits คือจำนวน classical bits ที่ถูกถ่ายโอน พิจารณาตัวอย่างต่อไปนี้:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    ในตัวอย่างโค้ดด้านบน if_test สองตัวแรกบน c0 ถือเป็น broadcast เดียวเพราะเนื้อหาของ c0 ไม่เปลี่ยนแปลง จึงไม่ต้อง broadcast ซ้ำ if_test บน c1 เป็น broadcast ที่สอง broadcast แรก broadcast บิตทั้งสามใน c0 และ broadcast ที่สอง broadcast เพียงหนึ่งบิต รวมเป็น broadcasted bits ทั้งหมดสี่บิต

    ปัจจุบัน ถ้า broadcast ครั้งละ 60 bits job สามารถมีได้ประมาณ 300 broadcasts แต่ถ้า broadcast ครั้งละหนึ่งบิต job สามารถมีได้ถึง 2400 broadcasts

  • operand ที่ใช้ในคำสั่ง if_test ต้องมี 32 bits หรือน้อยกว่า ดังนั้น ถ้าเปรียบเทียบ ClassicalRegister ทั้งหมด ขนาดของ ClassicalRegister นั้นต้องมี 32 bits หรือน้อยกว่า อย่างไรก็ตาม ถ้าเปรียบเทียบเพียงบิตเดียวจาก ClassicalRegister ClassicalRegister นั้นสามารถมีขนาดเท่าใดก็ได้ (เนื่องจาก operand มีเพียงหนึ่งบิต)

    ตัวอย่างเช่น code block "ไม่ถูกต้อง" ไม่ทำงานเพราะ cr มีมากกว่า 32 bits อย่างไรก็ตาม สามารถใช้ classical register ที่กว้างกว่า 32 bits ได้ถ้าทดสอบเพียงหนึ่งบิต ดังที่แสดงใน code block "ถูกต้อง"

       cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • ไม่อนุญาตให้ซ้อน conditionals ตัวอย่างเช่น code block ต่อไปนี้จะไม่ทำงานเพราะมี if_test อยู่ภายใน if_test อีกตัว:

       c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • ไม่รองรับ reset หรือการวัดภายใน conditionals

  • ไม่รองรับ arithmetic operations

  • ดู ตาราง feature ของ OpenQASM 3 เพื่อดูว่า feature ใดของ OpenQASM 3 ที่รองรับบน Qiskit และ Qiskit Runtime

  • เมื่อใช้ OpenQASM 3 (แทน QuantumCircuit) เป็นรูปแบบ input สำหรับส่ง circuits ไปยัง Qiskit Runtime primitives จะรองรับเฉพาะคำสั่งที่โหลดเข้า Qiskit ได้เท่านั้น ตัวอย่างเช่น classical operations ไม่รองรับเพราะโหลดเข้า Qiskit ไม่ได้ ดู Import an OpenQASM 3 program into Qiskit สำหรับข้อมูลเพิ่มเติม

  • ไม่รองรับคำสั่ง for, while และ switch

ใช้ dynamic circuits กับ Estimator

เนื่องจาก Estimator ไม่รองรับ dynamic circuits จึงสามารถใช้ Sampler และสร้าง measurement circuits เองได้ หรือจะใช้ Executor primitive, ซึ่งรองรับ dynamic circuits

หากต้องการจำลองพฤติกรรมของ Estimator ให้ทำตามขั้นตอนนี้:

  1. จัดกลุ่ม terms ของ observables ทั้งหมดเป็น partition ซึ่งทำได้โดยใช้ PauliList API, ตัวอย่างเช่น
    หมายเหตุ

    สามารถใช้ primitive attribute BitArray เพื่อคำนวณ expectation values ของ observables ที่ระบุได้

  2. รัน basis change circuit หนึ่ง circuit ต่อ partition (ไม่ว่าจะต้องเปลี่ยน basis แบบใดสำหรับแต่ละ partition) ดูข้อมูลเพิ่มเติมที่ addon utility สำหรับ measurement bases ใน measurement_bases module เริ่มต้นใช้งาน utilities
  3. รวมผลลัพธ์ของแต่ละ partition กลับเข้าด้วยกัน

ขั้นตอนถัดไป

คำแนะนำ
  • เรียนรู้วิธีใช้ dynamic decoupling อย่างแม่นยำโดยใช้ stretch
  • เรียนรู้เกี่ยวกับ mid-circuit measurements ที่สั้นกว่าซึ่งลดเวลาของ Circuit
  • ใช้ circuit schedule visualization เพื่อ debug และปรับแต่ง dynamic circuits ของคุณ
Source: IBM Quantum docs — updated 1 เม.ย. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569