Przejdź do głównej treści

Wykonywanie obwodów dynamicznych

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu następujących wymagań. Zalecamy używanie tych lub nowszych wersji.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1

Obwody dynamiczne to potężne narzędzia, za pomocą których możesz mierzyć kubity w środku wykonywania obwodu kwantowego, a następnie wykonywać klasyczne operacje logiczne wewnątrz obwodu, oparte na wynikach tych pomiarów śródkombinacyjnych. Proces ten jest również znany jako klasyczne sprzężenie zwrotne. Choć to są wczesne dni rozumienia, jak najlepiej korzystać z obwodów dynamicznych, społeczność badań kwantowych już zidentyfikowała wiele przypadków użycia, takich jak:

Ulepszenia wprowadzane przez obwody dynamiczne wiążą się jednak z kompromisami. Pomiary śródkombinacyjne i operacje klasyczne mają zazwyczaj dłuższy czas wykonania niż bramki dwuqubitowe, a ten wzrost czasu może niwelować korzyści ze zmniejszonej głębokości obwodu. Dlatego skracanie długości pomiaru śródkombinacyjnego jest priorytetowym obszarem ulepszeń, gdy IBM Quantum® wydaje nową wersję obwodów dynamicznych. W przypadku innych ograniczeń przy używaniu obwodów dynamicznych, sprawdź tabelę kompatybilności funkcji Estimator lub Sampler.

Specyfikacja OpenQASM 3 definiuje wiele struktur przepływu sterowania, ale Qiskit Runtime obecnie obsługuje tylko warunkową instrukcję if. W Qiskit SDK odpowiada to metodzie if_test na QuantumCircuit. Ta metoda zwraca menedżer kontekstu i jest zazwyczaj używana w instrukcji with. Ten przewodnik opisuje, jak używać tej instrukcji warunkowej.

uwaga

Przykłady kodu w tym przewodniku używają standardowej instrukcji miary do pomiarów śródkombinacyjnych. Zalecamy jednak używanie instrukcji MidCircuitMeasure, jeśli backend ją obsługuje. Szczegóły znajdziesz w sekcji Pomiary śródkombinacyjne.

Znajdź backendy obsługujące obwody dynamiczne

Aby znaleźć wszystkie backendy dostępne dla Twojego konta i obsługujące obwody dynamiczne, uruchom kod podobny do poniższego. Ten przykład zakłada, że zapisałeś dane logowania. Możesz również jawnie podać dane uwierzytelniające podczas inicjalizacji konta usługi Qiskit Runtime. Pozwoli Ci to na przykład przeglądać backendy dostępne w konkretnej instancji lub typie planu.

Uwagi
  • Backendy dostępne dla konta zależą od instancji podanej w danych uwierzytelniających.
  • Nowa wersja obwodów dynamicznych jest teraz dostępna dla wszystkich użytkowników na wszystkich backendach. Szczegóły znajdziesz w ogłoszeniu.
# 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')>]

Pomiary śródkombinacyjne

Przed qiskit-ibm-runtime v0.43.0, measure była jedyną instrukcją pomiaru w Qiskit. Pomiary śródkombinacyjne mają jednak inne wymagania strojenia niż pomiary końcowe (pomiary, które następują na końcu obwodu). Na przykład przy strojeniu pomiaru śródkombinacyjnego należy uwzględnić czas trwania instrukcji, ponieważ dłuższe instrukcje powodują bardziej zaszumione obwody. Dla pomiarów końcowych nie trzeba brać pod uwagę czasu trwania instrukcji, ponieważ po pomiarach końcowych nie ma żadnych instrukcji.

uwaga

Instrukcja MidCircuitMeasure mapuje się na instrukcję measure_2 raportowaną w supported_instructions backendu. Jednak measure_2 nie jest obsługiwane na wszystkich backendach. Użyj service.backends(filters=lambda b: "measure_2" in b.supported_instructions), aby znaleźć backendy, które go obsługują. W przyszłości mogą zostać dodane nowe pomiary, ale nie jest to gwarantowane.

Metoda MidCircuitMeasure

W qiskit-ibm-runtime v0.43.0 wprowadzono instrukcję MidCircuitMeasure. Jak sama nazwa wskazuje, jest to nowa instrukcja pomiaru zoptymalizowana pod kątem pomiarów śródkombinacyjnych na QPU IBM®. Choć możesz używać QuantumCircuit.measure do pomiaru śródkombinacyjnego, ze względu na projekt MidCircuitMeasure jest zazwyczaj lepszym wyborem. Na przykład dodaje mniejszy narzut do obwodu niż przy użyciu 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: ═══════════════════
Ważne uwagi
  • Musi istnieć co najmniej jeden rejestr klasyczny, aby używać pomiarów.
  • Prymityw Sampler wymaga pomiarów obwodu. Możesz dodawać pomiary obwodu z prymitywem Estimator, ale są one ignorowane.

Store

W wersji qiskit-ibm-runtime 0.47.0 lub nowszej możesz używać instrukcji store, aby zapisać wynik wyrażenia klasycznego, jeśli to wyrażenie będzie wielokrotnie używane. Operacje są automatycznie zrównoleglane, co sprawia, że kod jest znacznie bardziej wydajny w czasie wykonania.

Więcej informacji znajdziesz w przewodniku Klasyczne sprzężenie zwrotne i przepływ sterowania.

uwaga

Gdy używasz store do zapisania wartości do rejestru klasycznego na prawdziwym backendzie, wartość ta jest przechowywana tylko w pamięci podczas wykonania i nie jest kopiowana ani zwracana w wyniku zadania.

Na przykład w poniższym kodzie temp ma tę samą wartość co creg podczas wykonania, a if_test działa zgodnie z oczekiwaniami. Jednak po zakończeniu zadania BitArray wartości temp zwrócony w wyniku zadania nie zawiera wartości creg. Tzn. job.result()[0].data.temp wynosi 0.

creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...

Pełny przykład

Poniższy kod tworzy i uruchamia obwód dynamiczny na sprzęcie 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)

Ograniczenia Qiskit Runtime

Należy pamiętać o następujących ograniczeniach podczas uruchamiania obwodów dynamicznych w Qiskit Runtime.

  • Ze względu na ograniczoną pamięć fizyczną na elektronice sterującej istnieje również limit liczby instrukcji if i rozmiaru ich operandów. Ten limit jest funkcją liczby rozgłoszeń i liczby rozgłoszonych bitów w zadaniu (nie w obwodzie).

    Podczas przetwarzania warunku if dane pomiarów muszą być transferowane do logiki sterowania w celu dokonania tej oceny. Rozgłoszenie to transfer unikalnych danych klasycznych, a rozgłoszone bity to liczba transferowanych bitów klasycznych. Rozważ następujące:

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

    W poprzednim przykładzie kodu pierwsze dwa obiekty if_test na c0 są uważane za jedno rozgłoszenie, ponieważ zawartość c0 nie zmieniła się, a zatem nie musi być ponownie rozgłaszana. if_test na c1 to drugie rozgłoszenie. Pierwsze rozgłasza wszystkie trzy bity w c0, a drugie tylko jeden bit, co daje łącznie cztery rozgłoszone bity.

    Obecnie, jeśli rozgłaszasz 60 bitów za każdym razem, zadanie może mieć około 300 rozgłoszeń. Jeśli jednak rozgłaszasz tylko jeden bit za każdym razem, zadanie może mieć 2400 rozgłoszeń.

  • Operand używany w instrukcji if_test musi mieć co najwyżej 32 bity. Dlatego jeśli porównujesz cały ClassicalRegister, rozmiar tego ClassicalRegister musi wynosić co najwyżej 32 bity. Jeśli jednak porównujesz tylko jeden bit z ClassicalRegister, ten ClassicalRegister może mieć dowolny rozmiar (ponieważ operandem jest tylko jeden bit).

    Na przykład blok kodu "Niepoprawny" nie działa, ponieważ cr ma więcej niż 32 bity. Możesz jednak używać rejestru klasycznego szerszego niż 32 bity, jeśli testujesz tylko jeden bit, jak pokazano w bloku kodu "Poprawny".

    cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • Zagnieżdżone warunki nie są dozwolone. Na przykład poniższy blok kodu nie zadziała, ponieważ ma if_test wewnątrz innego if_test:

    c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • reset lub pomiary wewnątrz warunków nie są obsługiwane.

  • Operacje arytmetyczne nie są obsługiwane.

  • Zobacz tabelę funkcji OpenQASM 3, aby określić, które funkcje OpenQASM 3 są obsługiwane przez Qiskit i Qiskit Runtime.

  • Gdy OpenQASM 3 (zamiast QuantumCircuit) jest używany jako format wejściowy do przekazywania obwodów do prymitywów Qiskit Runtime, obsługiwane są tylko instrukcje, które można załadować do Qiskit. Na przykład operacje klasyczne nie są obsługiwane, ponieważ nie można ich załadować do Qiskit. Więcej informacji znajdziesz w Import programu OpenQASM 3 do Qiskit.

  • Instrukcje for, while i switch nie są obsługiwane.

Używanie obwodów dynamicznych z Estimator

Ponieważ Estimator nie obsługuje obwodów dynamicznych, możesz używać Sampler i budować własne obwody pomiarowe.

Aby odtworzyć zachowanie Estimator, wykonaj następujący proces:

  1. Podziel terminy wszystkich obserwabli na partycję. Można to zrobić na przykład za pomocą API PauliList.
    uwaga

    Możesz użyć atrybutu prymitywu BitArray do obliczania wartości oczekiwanych podanych obserwabli.

  2. Wykonaj jeden obwód zmiany bazy dla każdej partycji (niezależnie od tego, jaką zmianę bazy trzeba wykonać dla każdej partycji). Więcej informacji znajdziesz w module measurement_bases pakietu Measurement bases addon utility. Więcej informacji znajdziesz w dokumentacji pakietu Qiskit addon utilities.
  3. Dodaj z powrotem wyniki dla każdej partycji.

Ograniczenia

Przejrzyj dowolną tabelę kompatybilności funkcji, aby zrozumieć ograniczenia podczas używania obwodów dynamicznych. Pamiętaj, że kompatybilność funkcji nie zależy od prymitywu.

Następne kroki

Zalecenia