Přeskočit na hlavní obsah

Klasický feedforward a řízení toku (dynamické obvody)

Verze balíčků

Kód na této stránce byl vyvinut s použitím následujících závislostí. Doporučujeme používat tyto nebo novější verze.

qiskit[all]~=2.4.0

Dynamické obvody jsou mocné nástroje, pomocí nichž lze měřit Qubity uprostřed provádění kvantového Circuit a poté v rámci Circuit provádět operace klasické logiky na základě výsledků těchto průběžných měření. Tento postup se také nazývá klasický feedforward. Přestože je porozumění tomu, jak nejlépe využít dynamické obvody, teprve v počátcích, komunita kvantového výzkumu již identifikovala řadu případů použití, například:

Qiskit podporuje čtyři konstrukty řízení toku pro klasický feedforward, každý implementovaný jako metoda na QuantumCircuit. Konstrukty a jejich odpovídající metody jsou:

Každá z těchto metod vrací správce kontextu a obvykle se používá v příkazu with. Zbytek tohoto průvodce vysvětluje každý z těchto konstruktů a jak je používat.

pozor

Existují určitá omezení operací klasického feedforwardu a řízení toku na kvantovém hardwaru, která mohou ovlivnit tvůj program. Další informace najdeš v části Spuštění dynamických obvodů.

Příkaz if

Příkaz if se používá k podmíněnému provádění operací na základě hodnoty klasického bitu nebo registru.

V níže uvedeném příkladu aplikujeme Hadamardovu Gate na qubit a změříme jej. Pokud je výsledek 1, aplikujeme na qubit Gate X, čímž jej přepneme zpět do stavu 0. Poté qubit znovu změříme. Výsledný výsledek měření by měl být 0 se 100% pravděpodobností.

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

Výstup předchozí buňky kódu

Příkazu with lze přiřadit cíl, který je sám správcem kontextu a lze jej uložit a následně použít k vytvoření bloku else, jenž se provede vždy, když se obsah bloku if neprovede.

V níže uvedeném příkladu inicializujeme registry se dvěma Qubity a dvěma klasickými bity. Na první qubit aplikujeme Hadamardovu Gate a změříme jej. Pokud je výsledek 1, aplikujeme Hadamardovu Gate na druhý qubit; v opačném případě aplikujeme Gate X na druhý qubit. Nakonec změříme i druhý 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}

Výstup předchozí buňky kódu

Kromě podmíněnosti na jednom klasickém bitu je také možné podmíněnost vztáhnout na hodnotu klasického registru složeného z více bitů.

V níže uvedeném příkladu aplikujeme Hadamardovy Gate na dva Qubity a změříme je. Pokud je výsledek 01, tedy první qubit je 1 a druhý qubit je 0, aplikujeme Gate X na třetí qubit. Nakonec změříme třetí qubit. Všimni si, že pro přehlednost jsme se rozhodli specifikovat stav třetího klasického bitu, který je 0, v podmínce if. Ve schématu Circuit je podmínka znázorněna kruhy na klasických bitech, na které se podmínka vztahuje. Plný kruh označuje podmíněnost na 1, zatímco prázdný kruh označuje podmíněnost na 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}

Výstup předchozí buňky kódu

Příkaz switch

Příkaz switch se používá k výběru akcí na základě hodnoty klasického bitu nebo registru. Je podobný příkazu if, ale umožňuje zadat více případů pro logiku větvení. Níže uvedený příklad aplikuje Hadamardovu Gate na qubit a změří jej. Pokud je výsledek 0, aplikuje Gate X na qubit, a pokud je výsledek 1, aplikuje Gate Z. Výsledný výsledek měření by měl být 1 se 100% pravděpodobností.

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

Protože výše uvedený příklad používal jeden klasický bit, existovaly pouze dva možné případy, takže stejného výsledku bylo možné dosáhnout pomocí příkazu if-else. Příkaz switch je hlavně užitečný při větvení na základě hodnoty klasického registru složeného z více bitů. Následující příklad ukazuje, jak sestavit výchozí případ, který se provede, pokud žádný z předchozích případů neodpovídá. Všimni si, že v příkazu switch se vždy provede pouze jeden z bloků. Neexistuje žádný fallthrough.

Níže uvedený příklad aplikuje Hadamardovy Gate na dva Qubity a změří je. Pokud je výsledek 00 nebo 11, aplikuje Gate Z na třetí qubit. Pokud je výsledek 01, aplikuje Gate Y. Pokud žádný z předchozích případů neodpovídal, aplikuje Gate X. Nakonec změří třetí 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

Smyčka for

Smyčka for se používá k iteraci přes posloupnost klasických hodnot a provádění určitých operací během každé iterace.

Následující příklad používá smyčku for k aplikaci 5 Gate X na qubit a poté jej změří. Protože provádí lichý počet Gate X, celkový efekt je přepnutí Qubitu ze stavu 0 do stavu 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

Smyčka while

Smyčka while se používá k opakování instrukcí, dokud je splněna určitá podmínka.

Níže uvedený příklad aplikuje Hadamardovy Gate na dva Qubity a změří je. Poté vytvoří smyčku while, která tento postup opakuje, dokud je výsledek měření 11. Výsledné finální měření by tedy nikdy nemělo být 11, přičemž zbývající možnosti se vyskytují přibližně se stejnou frekvencí.

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

Klasické výrazy

Modul klasických výrazů Qiskit qiskit.circuit.classical obsahuje experimentální reprezentaci runtime operací na klasických hodnotách během provádění Circuit.

Následující příklad ukazuje, že výpočet parity lze použít k vytvoření n-Qubitového GHZ stavu pomocí dynamických obvodů. Nejprve vygeneruj n/2n/2 Bellových párů na sousedních Qubitech. Poté tyto páry slepuj dohromady pomocí vrstvy CNOT Gate mezi páry. Následně změř cílový qubit všech předchozích CNOT Gate a každý změřený qubit resetuj do stavu 0\vert 0 \rangle. Aplikuj XX na každé neměřené místo, pro které je parita všech předchozích bitů lichá. Nakonec se na změřené Qubity aplikují CNOT Gate, aby se obnovilo provázání ztracené při měření.

Při výpočtu parity první prvek konstruovaného výrazu zahrnuje zvednutí Python objektu mr[0] na uzel Value (lift se používá k převodu libovolných objektů na klasické výrazy). To není nutné pro mr[1] a případný následující klasický registr, protože jsou vstupem do expr.bit_xor a veškeré potřebné zvednutí se v těchto případech provede automaticky. Takové výrazy lze sestavovat ve smyčkách a dalších konstruktech.

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)

Výstup předchozí buňky kódu

Store

Instrukci store můžeš použít k uložení výsledku klasického výrazu, pokud bude tento výraz použit opakovaně. Operace jsou automaticky paralelizovány, což výrazně zvyšuje efektivitu kódu za běhu.

Například je přirozenější a efektivnější za běhu zapsat B[0]B[1]B[2]B[0] \oplus B[1] \oplus B[2] \ldots, kde B=¬AB = \neg A, než (¬A[0])(¬A[1])(¬A[2])(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots. První varianta vypočítá negaci v jediném paralelním kroku před řetězcem XOR, místo aby každou negaci vyhodnocovala postupně uvnitř výrazu.

Úplný příklad:

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

Další kroky

Doporučení