Execută 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 mai noi.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
Circuitele dinamice sunt instrumente puternice cu care poți măsura qubiții î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 mid-circuit. Acest proces este cunoscut și sub denumirea de feedforward clasic. Deși suntem în stadii incipiente de înțelegere a modului cel mai bun de a valorifica circuitele dinamice, comunitatea de cercetare cuantică a identificat deja o serie de cazuri de utilizare, cum ar fi:
- Pregătirea eficientă a stărilor cuantice, cum ar fi starea GHZ, starea W (pentru mai multe informații despre starea W, consultă și "State preparation by shallow circuits using feed forward") și o clasă largă de stări de tip produs matriceal
- Entanglement eficient la distanță mare între qubiții de pe același cip, folosind circuite superficiale
- Eșantionare eficientă a circuitelor de tip IQP
Aceste îmbunătățiri aduse de circuitele dinamice vin, însă, cu compromisuri. Măsurătorile mid-circuit și operațiile clasice au de obicei timpi de execuție mai lungi decât porțile cu doi qubiți, iar această creștere a timpului ar putea anula beneficiile aduse de reducerea adâncimii circuitului. Prin urmare, reducerea duratei măsurătorii mid-circuit este un domeniu de îmbunătățire pe care IBM Quantum® îl urmărește prin lansarea noii versiuni a circuitelor dinamice. Pentru alte restricții la folosirea circuitelor dinamice, consultă tabelul de compatibilitate a funcționalităților Estimator sau Sampler.
Specificația OpenQASM 3 definește o serie de structuri de control al fluxului, dar Qiskit Runtime suportă în prezent doar instrucțiunea condițională if. În Qiskit SDK, aceasta corespunde metodei if_test pe QuantumCircuit. Această metodă returnează un manager de context și este de obicei folosită într-o instrucțiune with. Acest ghid descrie cum să folosești această instrucțiune condițională.
Exemplele de cod din acest ghid folosesc instrucțiunea standard de măsurare pentru măsurătorile mid-circuit. Totuși, este recomandat să folosești în schimb instrucțiunea MidCircuitMeasure, dacă backend-ul o suportă. Consultă secțiunea Măsurători mid-circuit pentru detalii.
Găsește backend-uri care suportă circuite dinamice
Pentru a găsi toate backend-urile la care contul tău are acces și care suportă circuite dinamice, rulează cod similar cu cel de mai jos. Acest exemplu presupune că ți-ai salvat credențialele de autentificare. De asemenea, poți specifica explicit credențialele la inițializarea contului tău de serviciu Qiskit Runtime. Aceasta ți-ar permite, de exemplu, să vizualizezi backend-urile disponibile pe o anumită instanță sau tip de plan.
- Backend-urile disponibile pentru cont depind de instanța specificată în credențiale.
- Noua versiune a circuitelor dinamice este acum disponibilă pentru toți utilizatorii pe toate backend-urile. Consultă anunțul pentru mai multe detalii.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# This cell is hidden from users. It hides all those "...instance was not set..." warnings.
import warnings
warnings.filterwarnings("ignore", message=".*Instance was not set*")
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_kingston')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_boston')>]
Măsurători mid-circuit
Înainte de qiskit-ibm-runtime v0.43.0, measure era singura instrucțiune de măsurare din Qiskit. Măsurătorile mid-circuit au, însă, cerințe de acordare diferite față de măsurătorile terminale (măsurători care au loc la sfârșitul unui circuit). De exemplu, trebuie să iei în considerare durata instrucțiunii la acordarea unei măsurători mid-circuit, deoarece instrucțiunile mai lungi cauzează circuite mai zgomotoase. Nu trebuie să iei în considerare durata instrucțiunii pentru măsurătorile terminale, deoarece nu există instrucțiuni după măsurătorile terminale.
Instrucțiunea MidCircuitMeasure se mapează la instrucțiunea measure_2 raportată în supported_instructions a backend-ului. Totuși, measure_2 nu este suportată pe toate backend-urile. Folosește service.backends(filters=lambda b: "measure_2" in b.supported_instructions) pentru a găsi backend-urile care o suportă. Pot fi adăugate noi măsurători în viitor, dar acest lucru nu este garantat.
Metoda MidCircuitMeasure
În qiskit-ibm-runtime v0.43.0 a fost introdusă instrucțiunea MidCircuitMeasure. Așa cum sugerează numele, este o nouă instrucțiune de măsurare optimizată pentru mid-circuit pe QPU-urile IBM®. Deși poți folosi QuantumCircuit.measure pentru o măsurătoare mid-circuit, datorită design-ului său, MidCircuitMeasure este de obicei o alegere mai bună. De exemplu, adaugă mai puțin overhead circuitului tău față de cazul în care folosești QuantumCircuit.measure.
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)
circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
- Trebuie să existe cel puțin un registru clasic pentru a putea folosi măsurătorile.
- Primitiva Sampler necesită măsurători de circuit. Poți adăuga măsurători de circuit cu primitiva Estimator, dar acestea sunt ignorate.
Store
Cu versiunea qiskit-ibm-runtime 0.47.0 sau mai nouă, poți folosi instrucțiunea store pentru a salva rezultatul unei expresii clasice, dacă acea expresie va fi folosită în mod repetat. Operațiunile sunt paralelizate automat, făcând codul tău semnificativ mai eficient la runtime.
Pentru mai multe informații, consultă ghidul feedforward clasic și control flow.
Când folosești store pentru a salva o valoare într-un registru clasic pe un backend real, acea valoare este salvată doar în memorie în timpul execuției și nu este copiată sau returnată în rezultatul jobului.
De exemplu, în codul următor, temp are aceeași valoare ca creg în timpul execuției, iar if_test funcționează conform așteptărilor. Dar după finalizarea jobului, temp BitArray returnat în rezultatul jobului nu conține valoarea lui creg. Adică, job.result()[0].data.temp este 0.
creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...
Exemplu complet
Codul următor creează și rulează un circuit dinamic pe hardware IBM®.
from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)
# Create a dynamic circuit
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)
# Convert to an ISA circuit for the given backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
# Generate samplers for backend targets
sampler = SamplerV2(backend)
# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()
print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: d88cakp789is7391vq0g (DONE)
Limitările Qiskit Runtime
Fii conștient de următoarele constrângeri atunci când rulezi circuite dinamice în Qiskit Runtime.
-
Datorită memoriei fizice limitate din electronica de control, există și o limită pentru numărul de instrucțiuni
ifși dimensiunea operanzilor acestora. Această limită este o funcție a numărului de broadcast-uri și a numărului de biți broadcast-ați într-un job (nu într-un circuit).La procesarea unei condiții
if, datele de măsurare trebuie transferate la logica de control pentru a efectua acea evaluare. Un broadcast este un transfer de date clasice unice, iar biții broadcast-ați reprezintă numărul de biți clasici transferați. Consideră următoarele: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)) ...În exemplul de cod anterior, primele două obiecte
if_testpec0sunt considerate un singur broadcast deoarece conținutul luic0nu s-a schimbat și, prin urmare, nu trebuie re-broadcast-at.if_testpec1este un al doilea broadcast. Primul broadcast-ează toți cei trei biți dinc0, iar al doilea broadcast-ează un singur bit, rezultând un total de patru biți broadcast-ați.În prezent, dacă broadcast-ezi 60 de biți de fiecare dată, jobul poate avea aproximativ 300 de broadcast-uri. Dacă broadcast-ezi un singur bit de fiecare dată, totuși, jobul poate avea 2400 de broadcast-uri.
-
Operandul folosit într-o instrucțiune
if_testtrebuie să fie de 32 de biți sau mai puțin. Astfel, dacă compari un întregClassicalRegister, dimensiunea aceluiClassicalRegistertrebuie să fie de 32 de biți sau mai puțin. Dacă compari doar un singur bit dintr-unClassicalRegister, totuși, acelClassicalRegisterpoate fi de orice dimensiune (deoarece operandul este un singur bit).De exemplu, blocul de cod „Not valid" nu funcționează deoarece
crare mai mult de 32 de biți. Poți totuși folosi un registru clasic mai lat de 32 de biți dacă testezi doar un bit, așa cum se arată în blocul de cod „Valid".- Not valid
- Valid
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)):... -
Condiționalele imbricate nu sunt permise. De exemplu, următorul bloc de cod nu va funcționa deoarece conține un
if_testînăuntrul altuiif_test:- Not valid
- Valid
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)):... -
resetsau măsurătorile în interiorul condiționalelor nu sunt suportate. -
Operațiunile aritmetice nu sunt suportate.
-
Consultă tabelul de funcționalități OpenQASM 3 pentru a determina ce funcționalități OpenQASM 3 sunt suportate în Qiskit și Qiskit Runtime.
-
Când OpenQASM 3 (în loc de
QuantumCircuit) este folosit ca format de intrare pentru a transmite circuite la primitivele Qiskit Runtime, sunt suportate doar instrucțiunile care pot fi încărcate în Qiskit. Operațiile clasice, de exemplu, nu sunt suportate deoarece nu pot fi încărcate în Qiskit. Consultă Importă un program OpenQASM 3 în Qiskit pentru mai multe informații. -
Instrucțiunile
for,whileșiswitchnu sunt suportate.
Folosește circuite dinamice cu Estimator
Deoarece Estimator nu suportă circuite dinamice, poți folosi Sampler și construi propriile circuite de măsurare.
Pentru a replica comportamentul Estimator, urmează acest proces:
- Grupează termenii tuturor observabililor într-o partiție. Acest lucru se poate face folosind API-ul
PauliList, de exemplu.notăPoți folosi atributul primitiv
BitArraypentru a calcula valorile de așteptare ale observabililor furnizați. - Execută un circuit de schimbare a bazei per partiție (orice schimbare de bază trebuie efectuată pentru fiecare partiție). Consultă utilitarul addon pentru baze de măsurare modulul
measurement_basespentru mai multe informații. Pentru mai multe informații, consultă documentația pachetului de utilități Qiskit addon. - Adună înapoi rezultatele pentru fiecare partiție.
Restricții
Consultă orice Tabel de compatibilitate a funcționalităților pentru a înțelege restricțiile la folosirea circuitelor dinamice. Rețineți că compatibilitatea funcționalităților nu depinde de primitivă.
Pașii următori
- Află cum să implementezi dynamical decoupling precis folosind stretch.
- Consultă ghidul feedforward clasic și control flow.
- Folosește vizualizarea programului circuitului pentru a depana și optimiza circuitele tale dinamice.
- Nu toate funcțiile sunt compatibile cu circuitele dinamice. Verifică secțiunea de compatibilitate a funcționalităților pentru Sampler sau Executor pentru detalii.