Classical feedforward at control flow (dynamic circuits)
Package versions
Ang code sa pahinang ito ay ginawa gamit ang mga sumusunod na kinakailangan. Inirerekomenda naming gamitin ang mga bersyong ito o mas bago pa.
qiskit[all]~=2.4.0
Ang dynamic circuits ay makapangyarihang mga tool na maaari mong gamitin para sukatin ang mga qubit sa gitna ng pagpapatakbo ng quantum circuit at pagkatapos ay magsagawa ng mga classical logic operation sa loob ng circuit, batay sa resulta ng mga mid-circuit na pagsukat. Ang prosesong ito ay kilala rin bilang classical feedforward. Bagama't maaga pa lamang ang pag-unawa kung paano pinakamabuti na mapagsamantalahan ang dynamic circuits, ang komunidad ng quantum research ay nakakilala na ng ilang mga use case, tulad ng mga sumusunod:
- Mahusay na paghahanda ng quantum state, tulad ng GHZ state, W-state (para sa karagdagang impormasyon tungkol sa W-state, tingnan din ang "State preparation by shallow circuits using feed forward"), at isang malawak na klase ng matrix product states
- Mahusay na long-range entanglement sa pagitan ng mga qubit sa parehong chip sa pamamagitan ng paggamit ng mga mababaw na circuit
- Mahusay na sampling ng IQP-like circuits
Sinusuportahan ng Qiskit ang apat na control flow construct para sa classical feedforward, bawat isa ay ipinapatupad bilang isang paraan sa QuantumCircuit. Ang mga construct at ang kanilang kaukulang mga paraan ay:
- If statement -
QuantumCircuit.if_test - Switch statement -
QuantumCircuit.switch - For loop -
QuantumCircuit.for_loop - While loop -
QuantumCircuit.while_loop
Ang bawat isa sa mga pamamaraang ito ay nagbabalik ng isang context manager at karaniwang ginagamit sa isang with statement. Ipinaliwanag ng natitirang bahagi ng gabay na ito ang bawat isa sa mga construct na ito at kung paano ito gamitin.
Mayroong ilang mga limitasyon ng classical feedforward at control flow operation sa quantum hardware na maaaring makaapekto sa iyong programa. Para sa karagdagang impormasyon, tingnan ang I-execute ang mga dynamic circuit.
if statement
Ginagamit ang if statement para magsagawa ng mga operasyon nang kondisyonal batay sa halaga ng isang classical bit o register.
Sa halimbawa sa ibaba, nag-aaplay kami ng Hadamard gate sa isang qubit at sinusukat ito. Kung ang resulta ay 1, nag-aaplay kami ng X gate sa qubit, na may epektong ibabalik ito sa estado na 0. Pagkatapos ay sinusukat namin ang qubit muli. Ang nagreresultang kinalabasan ng pagsukat ay dapat na 0 na may 100% na posibilidad.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
circuit.h(q0)
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}
Ang with statement ay maaaring bigyan ng assignment target na isa ring context manager na maaaring i-store at pagkatapos ay gamitin para lumikha ng else block, na isinasagawa tuwing ang mga nilalaman ng if block ay hindi isinasagawa.
Sa halimbawa sa ibaba, nagpapasimula kami ng mga register na may dalawang qubit at dalawang classical bit. Nag-aaplay kami ng Hadamard gate sa unang qubit at sinusukat ito. Kung ang resulta ay 1, nag-aaplay kami ng Hadamard gate sa pangalawang qubit; kung hindi, nag-aaplay kami ng X gate sa pangalawang qubit. Sa wakas, sinusukat namin ang pangalawang qubit din.
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}
Bukod sa pag-condition sa isang classical bit, posible rin ang pag-condition sa halaga ng isang classical register na binubuo ng maraming bit.
Sa halimbawa sa ibaba, nag-aaplay kami ng Hadamard gate sa dalawang qubit at sinusukat ang mga ito. Kung ang resulta ay 01, iyon ay, ang unang qubit ay 1 at ang pangalawang qubit ay 0, nag-aaplay kami ng X gate sa ikatlong qubit. Sa wakas, sinusukat namin ang ikatlong qubit. Tandaan na para sa kalinawan, pinili naming tukuyin ang estado ng ikatlong classical bit, na 0, sa kondisyon ng if. Sa drawing ng circuit, ang kondisyon ay ipinahiwatig ng mga bilog sa mga classical bit na kina-condition. Ang isang solidong bilog ay nagpapahiwatig ng pag-condition sa 1, habang ang isang bilog na may balangkas ay nagpapahiwatig ng pag-condition sa 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}
Switch statement
Ginagamit ang switch statement para pumili ng mga aksyon batay sa halaga ng isang classical bit o register. Katulad ito ng isang if statement, ngunit maaari kang tumukoy ng mas maraming kaso para sa branching logic. Ang halimbawa sa ibaba ay nag-aaplay ng Hadamard gate sa isang qubit at sinusukat ito. Kung ang resulta ay 0, mag-aaplay ng X gate sa qubit, at kung ang resulta ay 1, mag-aaplay ng Z gate. Ang nagreresultang kinalabasan ng pagsukat ay dapat na 1 na may 100% na posibilidad.
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.switch(c0) as case:
with case(0):
circuit.x(q0)
with case(1):
circuit.z(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")
# example output counts: {'1': 1024}
Dahil gumamit ang halimbawa sa itaas ng isang classical bit lamang, dalawa lang ang posibleng kaso, kaya maaari mong makamit ang parehong resulta gamit ang isang if-else statement. Ang switch case ay pangunahing kapaki-pakinabang kapag nagba-branch sa halaga ng isang classical register na binubuo ng maraming bit. Ang sumusunod na halimbawa ay nagpapakita kung paano bumuo ng default case, na isinasagawa kung wala sa mga naunang kaso ang tumugma. Tandaan na sa isang switch statement, isa lamang sa mga block ang isinasagawa. Walang fallthrough.
Ang halimbawa sa ibaba ay nag-aaplay ng Hadamard gate sa dalawang qubit at sinusukat ang mga ito. Kung ang resulta ay alinman sa 00 o 11, mag-aaplay ng Z gate sa ikatlong qubit. Kung ang resulta ay 01, mag-aaplay ng Y gate. Kung wala sa mga naunang kaso ang tumugma, mag-aaplay ng X gate. Sa wakas, susukat ng ikatlong qubit.
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.switch(clbits) as case:
with case(0b000, 0b011):
circuit.z(q2)
with case(0b001):
circuit.y(q2)
with case(case.DEFAULT):
circuit.x(q2)
circuit.measure(q2, c2)
circuit.draw("mpl")
# example output counts: {'101': 267, '110': 249, '011': 265, '000': 243}
For loop
Ginagamit ang for loop para mag-iterate sa isang sequence ng mga classical value at magsagawa ng ilang operasyon sa bawat iteration.
Ang sumusunod na halimbawa ay gumagamit ng for loop para mag-apply ng 5 X gate sa isang qubit at pagkatapos ay susukat ito. Dahil nagsasagawa ito ng kakaibang bilang ng mga X gate, ang pangkalahatang epekto ay baligtarin ang qubit mula sa estado 0 patungo sa estado 1.
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
circuit = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
with circuit.for_loop(range(5)) as _:
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")
# example output counts: {'1': 1024}
While loop
Ginagamit ang while loop para ulitin ang mga instruksyon habang natutugunan ang ilang kondisyon.
Ang halimbawa sa ibaba ay nag-aaplay ng Hadamard gate sa dalawang qubit at sinusukat ang mga ito. Pagkatapos ay lumilikha ito ng while loop na umuulit ng pamamaraang ito habang ang kinalabasan ng pagsukat ay 11. Bilang resulta, ang panghuling pagsukat ay hindi dapat maging 11, at ang natitirang mga posibilidad ay lalabas na may humigit-kumulang pantay na dalas.
qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
q0, q1 = qubits
c0, c1 = clbits
circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.while_loop((clbits, 0b11)):
circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
circuit.draw("mpl")
# example output counts: {'01': 334, '10': 368, '00': 322}
Mga classical expression
Ang Qiskit classical expression module na qiskit.circuit.classical ay naglalaman ng eksplorasyon na representasyon ng mga runtime operation sa mga classical value sa panahon ng pagpapatakbo ng circuit.
Ang sumusunod na halimbawa ay nagpapakita na maaari mong gamitin ang pagkalkula ng parity para lumikha ng n-qubit GHZ state gamit ang dynamic circuits. Una, gumawa ng Bell pairs sa mga katabing qubit. Pagkatapos, idikit ang mga pares na ito gamit ang isang layer ng CNOT gate sa pagitan ng mga pares. Pagkatapos ay susukat ng mga target qubit ng lahat ng naunang CNOT gate at iri-reset ang bawat nasukat na qubit sa estado . Mag-aaplay ka ng sa bawat hindi nasukat na site kung saan ang parity ng lahat ng naunang bit ay kakaiba. Sa wakas, mga CNOT gate ang ilalapat sa mga nasukat na qubit para muling maitatag ang entanglement na nawala sa pagsukat.
Sa pagkalkula ng parity, ang unang elemento ng nabuong expression ay kinabibilangan ng pag-lift ng Python object na mr[0] sa isang Value node (lift ay ginagamit para gawing mga classical expression ang mga arbitrary na object). Hindi ito kinakailangan para sa mr[1] at sa posibleng susunod na classical register, dahil sila ay mga input sa expr.bit_xor, at ang anumang kinakailangang pag-lift ay awtomatikong ginagawa sa mga kasong ito. Ang mga ganitong expression ay maaaring itayo sa mga loop at iba pang mga construct.
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)
Store
Maaari mong gamitin ang instruksyong store upang i-save ang resulta ng isang classical expression, kung ang expression na iyon ay gagamitin nang paulit-ulit. Ang mga operasyon ay awtomatikong ina-parallelize, na ginagawang mas mahusay ang iyong code sa runtime.
Halimbawa, mas natural at mas mahusay sa runtime na isulat ang , kung saan ang , kaysa sa . Ang una ay kinakalkula ang negasyon sa isang parallel na hakbang bago ang XOR chain, sa halip na i-evaluate ang bawat negasyon nang sunod-sunod sa loob ng expression.
Buong halimbawa:
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
qregs = QuantumRegister(4, "q")
creg = ClassicalRegister(3, "c")
# temp is a plain ClassicalRegister used as the store target
temp = ClassicalRegister(3, "temp")
qc = QuantumCircuit(qregs, creg, temp)
qc.h([0, 1, 2])
qc.measure([0, 1, 2], creg)
# Store bit-NOT of the full 3-bit register into temp
qc.store(temp, expr.bit_not(creg))
# Compute parity of temp using bit-indexed XOR
parity = expr.bit_xor(
expr.bit_xor(expr.index(temp, 0), expr.index(temp, 1)),
expr.index(temp, 2),
)
# Flip q3 if parity of ~creg is 1
with qc.if_test(parity):
qc.x(3)
qc.measure([0, 1, 2], creg)
qc.draw("mpl")
Mga susunod na hakbang
- Alamin kung paano mag-implement ng tumpak na dynamic decoupling sa pamamagitan ng paggamit ng stretch.
- Gamitin ang circuit schedule visualization para i-debug at i-optimize ang iyong mga dynamic circuit.
- I-execute ang mga dynamic circuit.