Sari la conținutul principal

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:

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ă.

notă

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.

Note
  • 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.

notă

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: ═══════════════════
Note importante
  • 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.

notă

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_test pe c0 sunt considerate un singur broadcast deoarece conținutul lui c0 nu s-a schimbat și, prin urmare, nu trebuie re-broadcast-at. if_test pe c1 este un al doilea broadcast. Primul broadcast-ează toți cei trei biți din c0, 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_test trebuie să fie de 32 de biți sau mai puțin. Astfel, dacă compari un întreg ClassicalRegister, dimensiunea acelui ClassicalRegister trebuie să fie de 32 de biți sau mai puțin. Dacă compari doar un singur bit dintr-un ClassicalRegister, totuși, acel ClassicalRegister poate fi de orice dimensiune (deoarece operandul este un singur bit).

    De exemplu, blocul de cod „Not valid" nu funcționează deoarece cr are 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".

    cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • 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 altui if_test:

    c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • reset sau 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 și switch nu 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:

  1. 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 BitArray pentru a calcula valorile de așteptare ale observabililor furnizați.

  2. 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_bases pentru mai multe informații. Pentru mai multe informații, consultă documentația pachetului de utilități Qiskit addon.
  3. 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

Recomandări