Przejdź do głównej treści

Wprowadzenie do prymitywów

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
Nowy model wykonania — teraz w wersji beta

Wersja beta nowego modelu wykonania jest już dostępna. Ukierunkowany model wykonania zapewnia większą elastyczność przy dostosowywaniu przepływu pracy związanego z mitygacją błędów. Więcej informacji znajdziesz w przewodniku Ukierunkowany model wykonania.

Dlaczego Qiskit wprowadził prymitywy?

Podobnie jak we wczesnych dniach komputerów klasycznych, gdy programiści musieli bezpośrednio manipulować rejestrami procesora, pierwotny interfejs QPU po prostu zwracał surowe dane z elektroniki sterującej. Nie stanowiło to większego problemu, gdy QPU znajdowały się w laboratoriach i były dostępne wyłącznie dla badaczy. Uznając, że większość programistów nie powinna musieć znać szczegółów konwersji surowych danych na 0 i 1, Qiskit wprowadził backend.run — pierwszą abstrakcję umożliwiającą dostęp do QPU w chmurze. Pozwoliło to programistom pracować na znajomym formacie danych i skupić się na szerszym obrazie.

Gdy dostęp do QPU stał się powszechniejszy i zaczęto opracowywać coraz więcej algorytmów kwantowych, znów pojawiła się potrzeba abstrakcji wyższego poziomu. W odpowiedzi Qiskit wprowadził interfejs prymitywów, zoptymalizowany pod kątem dwóch kluczowych zadań w tworzeniu algorytmów kwantowych: szacowania wartości oczekiwanych (Estimator) i próbkowania obwodów (Sampler). Celem jest ponownie pomoc programistom w skupieniu się na innowacjach, a nie na konwersji danych. Interfejs prymitywów zastępuje interfejs backend.run, ponieważ Sampler zapewnia ten sam bezpośredni dostęp do sprzętu, który oferował backend.run.

Czym jest prymityw?

Systemy obliczeniowe są zbudowane na wielu warstwach abstrakcji. Abstrakcje pozwalają skupić się na poziomie szczegółowości istotnym dla wykonywanego zadania. Im bliżej sprzętu, tym niższy poziom abstrakcji jest potrzebny (np. może być konieczne przenoszenie lub manipulowanie danymi na poziomie instrukcji procesora). Im bardziej złożone zadanie chcesz wykonać, tym wyższy poziom abstrakcji będzie wymagany (np. możesz korzystać z biblioteki programistycznej do wykonywania obliczeń algebraicznych).

W tym kontekście prymityw to najmniejsza instrukcja przetwarzania — najprostszy element składowy, z którego można stworzyć coś użytecznego na danym poziomie abstrakcji.

Ostatni postęp w obliczeniach kwantowych zwiększył potrzebę pracy na wyższych poziomach abstrakcji. W miarę jak dziedzina zmierza ku większym jednostkom przetwarzania kwantowego (QPU) i bardziej złożonym przepływom pracy, punkt ciężkości przesuwa się z interakcji z sygnałami poszczególnych qubitów na traktowanie urządzeń kwantowych jako systemów wykonujących określone zadania.

Dwa najczęstsze zadania komputerów kwantowych to próbkowanie stanów kwantowych i obliczanie wartości oczekiwanych. Te zadania stały się motywacją do zaprojektowania prymitywów Qiskit: Estimator i Sampler.

  • Estimator oblicza wartości oczekiwane obserwowalnych względem stanów przygotowanych przez Circuit.
  • Sampler próbkuje rejestr wyjściowy z wykonania Circuit.

Krótko mówiąc, model obliczeniowy wprowadzony przez prymitywy Qiskit przybliża programowanie kwantowe do tego, czym jest dziś programowanie klasyczne — gdzie skupiamy się mniej na szczegółach sprzętowych, a bardziej na wynikach, które chcemy osiągnąć.

Definicja prymitywów i ich implementacje

Istnieją dwa rodzaje prymitywów Qiskit: klasy bazowe oraz ich implementacje. Prymitywy Qiskit są zdefiniowane przez klasy bazowe prymitywów o otwartym kodzie źródłowym, które znajdują się w Qiskit SDK (w module qiskit.primitives). Dostawcy (tacy jak Qiskit Runtime) mogą używać tych klas bazowych do tworzenia własnych implementacji Sampler i Estimator. Większość użytkowników korzysta z implementacji dostawców, a nie bezpośrednio z klas bazowych.

Klasy bazowe

BaseEstimatorV2 i BaseSamplerV2 — abstrakcyjne klasy bazowe definiujące wspólny interfejs do implementowania prymitywów. Wszystkie inne klasy w module qiskit.primitives dziedziczą po tych klasach bazowych. Programiści powinni z nich korzystać, jeśli chcą tworzyć własny model wykonania oparty na prymitywach dla konkretnego dostawcy. Klasy te mogą być również przydatne dla osób, które chcą wykonywać wysoko spersonalizowane przetwarzanie i uważają, że istniejące implementacje prymitywów są dla nich zbyt proste. Zwykli użytkownicy nie będą bezpośrednio korzystać z klas bazowych.

Implementacje

Oto implementacje klas bazowych prymitywów:

  • Prymitywy Qiskit Runtime (EstimatorV2 i SamplerV2) zapewniają bardziej zaawansowaną implementację (na przykład z uwzględnieniem mitygacji błędów) jako usługę w chmurze. Ta implementacja klas bazowych prymitywów służy do uzyskiwania dostępu do sprzętu IBM Quantum®. Dostęp do nich odbywa się przez IBM Qiskit Runtime.

  • StatevectorEstimator i StatevectorSampler — implementacje referencyjne prymitywów korzystające z symulatora wbudowanego w Qiskit. Są zbudowane przy użyciu modułu Qiskit quantum_info, generując wyniki na podstawie idealnych symulacji wektorów stanu. Dostęp do nich odbywa się przez Qiskit.

  • BackendEstimatorV2 i BackendSamplerV2 — możesz użyć tych klas, aby „opakować" dowolny zasób obliczeniowy kwantowy w prymityw. Dzięki temu możesz pisać kod w stylu prymitywów dla dostawców, którzy nie mają jeszcze interfejsu opartego na prymitywach. Klas tych można używać tak samo jak zwykłego Sampler i Estimator, z tym że należy je inicjalizować z dodatkowym argumentem backend wskazującym, na którym komputerze kwantowym uruchomić obliczenia. Dostęp do nich odbywa się przez Qiskit.

Zalety prymitywów Qiskit

Dzięki prymitywom użytkownicy Qiskit mogą pisać kod kwantowy dla konkretnego QPU bez konieczności jawnego zarządzania każdym szczegółem. Ponadto dodatkowa warstwa abstrakcji może ułatwić dostęp do zaawansowanych możliwości sprzętowych danego dostawcy. Na przykład, korzystając z prymitywów Qiskit Runtime, możesz skorzystać z najnowszych osiągnięć w zakresie mitygacji i tłumienia błędów, przełączając opcje takie jak resilience_level prymitywu, zamiast tworzyć własną implementację tych technik.

Dla dostawców sprzętu natywna implementacja prymitywów oznacza możliwość zapewnienia użytkownikom bardziej „gotowego do użycia" sposobu dostępu do funkcji sprzętowych, takich jak zaawansowane techniki post-processingu. Dzięki temu użytkownicy mogą łatwiej korzystać z najlepszych możliwości Twojego sprzętu.

Szczegóły prymitywów

Jak opisano wcześniej, wszystkie prymitywy są tworzone na podstawie klas bazowych, dlatego mają tę samą ogólną strukturę i sposób użycia. Na przykład format danych wejściowych dla wszystkich prymitywów Estimator jest taki sam. Istnieją jednak różnice w implementacjach, które sprawiają, że każda z nich jest wyjątkowa.

uwaga

Ponieważ większość użytkowników korzysta z prymitywów Qiskit Runtime, przykłady w dalszej części tej sekcji są oparte na prymitywach Qiskit Runtime.

Estimator

Prymityw Estimator oblicza wartości oczekiwane jednej lub więcej obserwowalnych względem stanów przygotowanych przez Circuit. Obwody mogą być sparametryzowane, o ile wartości parametrów są również podane jako dane wejściowe do prymitywu.

Dane wejściowe to tablica PUBów. Każdy PUB ma format:

(<pojedynczy circuit>, <jedna lub więcej obserwowalnych>, <opcjonalna jedna lub więcej wartości parametrów>, <opcjonalna precyzja>),

gdzie opcjonalne wartości parametrów mogą być listą lub pojedynczym parametrem. Różne implementacje Estimator obsługują różne opcje konfiguracji. Jeśli dane wejściowe zawierają pomiary, są one ignorowane.

Dane wyjściowe to PubResult zawierający obliczone wartości oczekiwane dla każdej pary oraz ich błędy standardowe w postaci PubResult. Każdy PubResult zawiera zarówno dane, jak i metadane.

Estimator łączy elementy obserwowalnych i wartości parametrów zgodnie z regułami rozgłaszania NumPy opisanymi w temacie Dane wejściowe i wyjściowe prymitywów.

Przykład:

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# This cell is hidden from users, it creates the circuits and observables to run

from qiskit_ibm_runtime import EstimatorV2, SamplerV2, QiskitRuntimeService
from qiskit.circuit.random import random_circuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np

service = QiskitRuntimeService()
backend = service.least_busy()
phi = Parameter("phi")

circuit1 = random_circuit(10, 5, seed=12345)
circuit1.rzz(phi, 1, 2)
observable1 = SparsePauliOp.from_sparse_list(
[("ZXYZ", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values1 = np.random.uniform(size=5).T

circuit2 = random_circuit(10, 5, seed=12345)
circuit2.rzz(phi, 1, 2)
observable2 = SparsePauliOp.from_sparse_list(
[("XZYX", [1, 2, 3, 4], 1)], num_qubits=10
)
param_values2 = np.random.uniform(size=5).T

shots1 = 164
shots2 = 1024

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
circuit1 = pm.run(circuit1)
circuit2 = pm.run(circuit2)
observable1 = observable1.apply_layout(circuit1.layout)
observable2 = observable2.apply_layout(circuit2.layout)
estimator = EstimatorV2(mode=backend)
estimator_job = estimator.run(
[
(circuit1, observable1, param_values1),
(circuit2, observable2, param_values2),
]
)

Sampler

Głównym zadaniem Sampler jest próbkowanie rejestru wyjściowego z wykonania jednego lub więcej Circuit. Wejściowe Circuit mogą być sparametryzowane, o ile wartości parametrów są również podane jako dane wejściowe do prymitywu.

Dane wejściowe to jeden lub więcej PUBów, w formacie:

(<pojedynczy circuit>, <jedna lub więcej opcjonalnych wartości parametrów>, <opcjonalna liczba strzałów>),

gdzie może być wiele elementów wartości parametrów, a każdy element może być tablicą lub pojedynczym parametrem, w zależności od wybranego Circuit. Ponadto dane wejściowe muszą zawierać pomiary.

Dane wyjściowe to liczniki lub pomiary dla każdego strzału, jako obiekty PubResult, bez wag. Klasa wynikowa ma jednak metody zwracające ważone próbki, takie jak liczniki. Pełne szczegóły znajdziesz w sekcji Dane wejściowe i wyjściowe prymitywów.

Przykład:

# This cell is hidden from users, add measurement instructions to circuits
circuit1.measure_active()
circuit2.measure_active()
sampler = SamplerV2(mode=backend)
sampler_job = sampler.run(
[
(circuit1, param_values1, shots1),
(circuit2, param_values2, shots2),
]
)

Następne kroki

Rekomendacje