Klasický feedforward a řízení toku
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.3.0
qiskit-ibm-runtime~=0.43.1
Nová verze dynamických obvodů je nyní dostupná všem uživatelům na všech Backend zařízeních. Dynamické obvody teď můžeš spouštět v užitkovém měřítku. Více informací najdeš v oznámení.
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:
- Efektivní příprava kvantových stavů, například GHZ stav, W-stav, (další informace o W-stavu najdeš také v "State preparation by shallow circuits using feed forward") a rozsáhlá třída maticových produktových stavů
- Efektivní provázání na velké vzdálenosti mezi Qubity na stejném čipu pomocí mělkých obvodů
- Efektivní vzorkování obvodů podobných IQP
Tato zlepšení přinášená dynamickými obvody jsou však spojena s kompromisy. Průběžná měření a klasické operace mají zpravidla delší dobu provádění než dvoQubitové Gate, a tento nárůst doby může negovat výhody snížené hloubky Circuit. Proto je zkrácení délky průběžného měření oblastí zlepšení, na níž IBM Quantum® pracuje v rámci nové verze dynamických obvodů.
Specifikace OpenQASM 3 definuje řadu struktur pro řízení toku, ale Qiskit Runtime v současnosti podporuje pouze podmíněný příkaz if. V Qiskit SDK toto odpovídá metodě if_test na QuantumCircuit. Tato metoda vrací správce kontextu a typicky se používá v příkazu with. Tento průvodce popisuje, jak tento podmíněný příkaz používat.
Příklady kódu v tomto průvodci používají standardní instrukci measure pro průběžná měření. Doporučuje se však místo toho použít instrukci MidCircuitMeasure, pokud ji Backend podporuje. Podrobnosti najdeš v dokumentaci průběžných měření.
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 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}
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}
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. Černý kruh označuje podmíněnost na 1, zatímco bílý 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}
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. Kvůli hardwarovým omezením jsou v současnosti podporovány pouze podmínky QuantumCircuit.if_test().
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 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 . Aplikuj 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)
Vyhledání Backend zařízení podporujících dynamické obvody
Chceš-li najít všechna Backend zařízení, ke kterým má tvůj účet přístup a která podporují dynamické obvody, spusť kód podobný následujícímu. Tento příklad předpokládá, že máš uloženy přihlašovací údaje. Můžeš také explicitně zadat přihlašovací údaje při inicializaci účtu Qiskit Runtime service. To ti například umožní zobrazit Backend zařízení dostupná pro konkrétní instanci nebo typ plánu.
- Backend zařízení dostupná pro účet závisí na instanci zadané v přihlašovacích údajích.
- Nová verze dynamických obvodů je nyní dostupná všem uživatelům na všech Backend zařízeních. Více informací najde š v oznámení.
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')>]
Omezení Qiskit Runtime
Při spouštění dynamických obvodů v Qiskit Runtime si uvědom následující omezení.
-
Kvůli omezené fyzické paměti řídicí elektroniky existuje také limit na počet příkazů
ifa velikost jejich operandů. Tento limit je funkcí počtu přenosů a počtu přenášených bitů v úloze (nikoli v Circuit).Při zpracování podmínky
ifje nutné přenést měřicí data do řídicí logiky, aby bylo možné toto vyhodnocení provést. Přenos je přenesení jedinečných klasických dat a přenášené bity jsou počet přenášených klasických bitů. Zvažte následující: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)) ...V předchozím příkladu kódu se první dva objekty
if_testnac0považují za jeden přenos, protože obsahc0se nezměnil, a tedy není nutné jej znovu přenášet.if_testnac1je druhý přenos. První přenáší všechny tři bity vc0a druhý přenáší pouze jeden bit, celkem tedy čtyři přenášené bity.V současnosti, pokud přenášíš 60 bitů pokaždé, může mít úloha přibližně 300 přenosů. Pokud však přenášíš pokaždé pouze jeden bit, může mít úloha 2400 přenosů.
-
Operand použitý v příkazu
if_testmusí mít 32 nebo méně bitů. Pokud tedy porovnáváš celýClassicalRegister, velikost tohotoClassicalRegistermusí být 32 nebo méně bitů. Pokud však porovnáváš pouze jeden bit zClassicalRegister, může mít tentoClassicalRegisterlibovolnou velikost (protože operand je pouze jeden bit).Například blok kódu "Neplatné" nefunguje, protože
crmá více než 32 bitů. Klasický registr širší než 32 bitů však lze použít, pokud testuješ pouze jeden bit, jak je ukázáno v bloku kódu "Platné".- Neplatné
- Platné
cr = ClassicalRegister(50)
qr = QuantumRegister(50)
circuit = QuantumCircuit(qr, cr)
...
circ.measure(qr, cr)
with circ.if_test((cr, 15)):
...cr = ClassicalRegister(50)
qr = QuantumRegister(50)
circuit = QuantumCircuit(qr, cr)
...
circ.measure(qr, cr)
with circ.if_test((cr[5], 1)):
... -
Vnořené podmínky nejsou povoleny. Například následující blok kódu nebude fungovat, protože obsahuje
if_testuvnitř jinéhoif_test:- Neplatné
- Platné
c1 = ClassicalRegister(1, "c1")
c2 = ClassicalRegister(2, "c2")
...
with circ.if_test((c1, 1)):
with circ.if_test(c2, 1)):
...cr = ClassicalRegister(2)
...
with circuit.if_test((cr, 0b11)):
... -
Použití
resetnebo měření uvnitř podmínek není podporováno. -
Aritmetické operace nejsou podporovány.
-
Informace o tom, které funkce OpenQASM 3 jsou podporovány v Qiskit a Qiskit Runtime, najdeš v tabulce funkcí OpenQASM 3.
-
Pokud se jako vstupní formát pro předávání Circuit primitivům Qiskit Runtime používá OpenQASM 3 (namísto
QuantumCircuit), jsou podporovány pouze instrukce, které lze načíst do Qiskit. Klasické operace například nejsou podporovány, protože je nelze načíst do Qiskit. Další informace najdeš v části Import programu OpenQASM 3 do Qiskit. -
Instrukce
for,whileaswitchnejsou podporovány.
Použití dynamických obvodů s Estimator
Protože Estimator nepodporuje dynamické obvody, můžeš místo toho použít Sampler a sestavit vlastní měřicí obvody. Případně můžeš použít primitivu Executor, která dynamické obvody podporuje.
Chceš-li replikovat chování Estimator, postupuj takto:
- Roztřiď členy všech observabilů do oddílu. To lze provést například pomocí API
PauliList,.poznámkaK výpočtu očekávaných hodnot poskytnutých observabilů můžeš použít atribut primitiva
BitArray. - Pro každý oddíl proveď jeden obvod změny báze (jakoukoli změnu báze, kterou je třeba provést pro každý oddíl). Více informací najdeš v doplňkovém nástroji pro měřicí báze, modulu
measurement_bases. Začni s nástroji. - Výsledky pro každý oddíl sečti zpět dohromady.
Další kroky
- Nauč se implementovat přesné dynamické oddělení pomocí stretch.
- Zjisti více o kratších průběžných měřeních, která zkracují dobu provádění Circuit.
- Použij vizualizaci časování Circuit k ladění a optimalizaci svých dynamických obvodů.