Sari la conținutul principal

Feedforward clasic și flux de control (circuite dinamice)

Versiuni de pachete

Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Recomandăm să folosești aceste versiuni sau versiuni mai noi.

qiskit[all]~=2.4.0

Circuitele dinamice sunt instrumente puternice cu care poți măsura qubiți în mijlocul execuției unui Circuit cuantic și apoi efectua operații de logică clasică în cadrul circuitului, pe baza rezultatelor acelor măsurători la mijlocul circuitului. Acest proces este cunoscut și sub denumirea de feedforward clasic. Deși suntem la început în înțelegerea modului optim de a profita de circuitele dinamice, comunitatea de cercetare cuantică a identificat deja o serie de cazuri de utilizare, precum:

Qiskit suportă patru construcții de flux de control pentru feedforward clasic, fiecare implementată ca metodă pe QuantumCircuit. Construcțiile și metodele corespunzătoare sunt:

Fiecare dintre aceste metode returnează un manager de context și este folosită de obicei într-o instrucțiune with. Restul acestui ghid explică fiecare dintre aceste construcții și cum să le folosești.

atenție

Există unele limitări ale operațiilor de feedforward clasic și flux de control pe hardware cuantic care pot afecta programul tău. Pentru mai multe informații, consultă Execută circuite dinamice.

Instrucțiunea if

Instrucțiunea if este folosită pentru a efectua condiționat operații pe baza valorii unui bit sau registru clasic.

În exemplul de mai jos, aplicăm o poartă Hadamard unui qubit și îl măsurăm. Dacă rezultatul este 1, atunci aplicăm o poartă X pe qubit, ceea ce are efectul de a-l readuce la starea 0. Măsurăm apoi qubit-ul din nou. Rezultatul măsurătorii ar trebui să fie 0 cu probabilitate de 100%.

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

Output of the previous code cell

Instrucțiunii with i se poate atribui o țintă care este ea însăși un manager de context, ce poate fi stocat și utilizat ulterior pentru a crea un bloc else, care se execută ori de câte ori conținutul blocului if nu este executat.

În exemplul de mai jos, inițializăm registre cu doi qubiți și două biți clasici. Aplicăm o poartă Hadamard primului qubit și îl măsurăm. Dacă rezultatul este 1, atunci aplicăm o poartă Hadamard pe al doilea qubit; altfel, aplicăm o poartă X pe al doilea qubit. În final, măsurăm și al doilea 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

Pe lângă condiționarea pe un singur bit clasic, este posibil și să condiționezi pe valoarea unui registru clasic compus din mai mulți biți.

În exemplul de mai jos, aplicăm porți Hadamard la doi qubiți și îi măsurăm. Dacă rezultatul este 01, adică primul qubit este 1 și al doilea qubit este 0, atunci aplicăm o poartă X unui al treilea qubit. În final, măsurăm al treilea qubit. Remarcă că, pentru claritate, am ales să specificăm starea celui de-al treilea bit clasic, care este 0, în condiția if. În desenul circuitului, condiția este indicată prin cercuri pe biții clasici pe care se condiționează. Un cerc plin indică condiționarea pe 1, în timp ce un cerc cu contur indică condiționarea pe 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

Instrucțiunea switch

Instrucțiunea switch este folosită pentru a selecta acțiuni pe baza valorii unui bit sau registru clasic. Este similară cu o instrucțiune if, dar poți specifica mai multe cazuri pentru logica de ramificare. Exemplul de mai jos aplică o poartă Hadamard unui qubit și îl măsoară. Dacă rezultatul este 0, aplică o poartă X pe qubit, iar dacă rezultatul este 1, aplică o poartă Z. Rezultatul măsurătorii ar trebui să fie 1 cu probabilitate de 100%.

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}

Output of the previous code cell

Deoarece exemplul de mai sus a folosit un singur bit clasic, au existat doar două cazuri posibile, deci ai fi putut obține același rezultat folosind o instrucțiune if-else. Cazul switch este util în principal când ramifici în funcție de valoarea unui registru clasic compus din mai mulți biți. Următorul exemplu arată cum să construiești un caz implicit, care se execută dacă niciunul dintre cazurile precedente nu se potrivește. Reține că într-o instrucțiune switch, se execută doar unul dintre blocuri. Nu există fallthrough.

Exemplul de mai jos aplică porți Hadamard la doi qubiți și îi măsoară. Dacă rezultatul este fie 00, fie 11, aplică o poartă Z celui de-al treilea qubit. Dacă rezultatul este 01, aplică o poartă Y. Dacă niciunul dintre cazurile precedente nu s-a potrivit, aplică o poartă X. În final, măsoară al treilea 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}

Output of the previous code cell

Bucla for

O buclă for este folosită pentru a itera peste o secvență de valori clasice și a efectua unele operații în cadrul fiecărei iterații.

Următorul exemplu folosește o buclă for pentru a aplica 5 porți X unui qubit și apoi îl măsoară. Deoarece efectuează un număr impar de porți X, efectul general este să inverseze qubit-ul din starea 0 în starea 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}

Output of the previous code cell

Bucla while

O buclă while este folosită pentru a repeta instrucțiuni cât timp o condiție este satisfăcută.

Exemplul de mai jos aplică porți Hadamard la doi qubiți și îi măsoară. Apoi creează o buclă while care repetă această procedură cât timp rezultatul măsurătorii este 11. Ca rezultat, măsurătoarea finală nu ar trebui să fie niciodată 11, celelalte posibilități apărând cu frecvență aproximativ egală.

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}

Output of the previous code cell

Expresii clasice

Modulul de expresii clasice Qiskit qiskit.circuit.classical conține o reprezentare exploratorie a operațiilor de execuție pe valori clasice în timpul execuției circuitului.

Următorul exemplu arată că poți folosi calculul parității pentru a crea o stare GHZ cu n qubiți folosind circuite dinamice. Mai întâi, generează n/2n/2 perechi Bell pe qubiți adiacenți. Apoi, lipește aceste perechi împreună folosind un strat de porți CNOT între perechi. Măsori apoi qubit-ul țintă al tuturor porților CNOT anterioare și resetezi fiecare qubit măsurat la starea 0\vert 0 \rangle. Aplici XX la fiecare poziție nemăsurată pentru care paritatea tuturor biților precedenți este impară. În final, porțile CNOT sunt aplicate qubiților măsurați pentru a restabili entanglement-ul pierdut în măsurătoare.

În calculul parității, primul element al expresiei construite implică ridicarea obiectului Python mr[0] la un nod Value (lift este folosit pentru a transforma obiecte arbitrare în expresii clasice). Acest lucru nu este necesar pentru mr[1] și posibilul registru clasic următor, deoarece acestea sunt intrări pentru expr.bit_xor, iar orice ridicare necesară este efectuată automat în aceste cazuri. Astfel de expresii pot fi construite în bucle și alte constructe.

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

Store

Poți utiliza instrucțiunea store pentru a salva rezultatul unei expresii clasice, dacă acea expresie va fi utilizată în mod repetat. Operațiile sunt paralelizate automat, făcând codul tău semnificativ mai eficient la rulare.

De exemplu, este mai natural și mai eficient la rulare să scrii B[0]B[1]B[2]B[0] \oplus B[1] \oplus B[2] \ldots, unde B=¬AB = \neg A, decât (¬A[0])(¬A[1])(¬A[2])(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots. Prima formă calculează negarea într-un singur pas paralel înainte de lanțul XOR, în loc să evalueze fiecare negare secvențial în interiorul expresiei.

Exemplu complet:

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

Output of the previous code cell

Pași următori

Recomandări