Przejdź do głównej treści

Migracja do prymitywów Qiskit Runtime V2

ostrzeżenie

Oryginalne prymitywy (określane jako prymitywy V1), V1 Sampler i V1 Estimator, zostały oznaczone jako przestarzałe w qiskit-ibm-runtime 0.23. Ich wsparcie zostało usunięte 15 sierpnia 2024 roku.

W związku z wycofaniem prymitywów V1 cały kod powinien zostać zmigrowany do interfejsów V2. Ten przewodnik opisuje, co zmieniło się w prymitywach Qiskit Runtime V2 (dostępnych od qiskit-ibm-runtime 0.21.0) i dlaczego, szczegółowo omawia każdy nowy prymityw oraz zawiera przykłady pomagające w migracji kodu z przestarzałych prymitywów do V2. Wszystkie przykłady w przewodniku używają prymitywów Qiskit Runtime, jednak ogólnie te same zmiany dotyczą też innych implementacji prymitywów. Funkcje unikalne dla Qiskit Runtime, takie jak ograniczanie błędów, pozostają wyłącznie w Qiskit Runtime.

Informacje o zmianach w prymitywach referencyjnych Qiskit (teraz zwanych prymitywami statevector) znajdziesz w sekcji qiskit.primitives na stronie zmian funkcji Qiskit 1.0. Referencyjne implementacje prymitywów V2 opisano w dokumentacji StatevectorSampler i StatevectorEstimator.

Przegląd

Wersja 2 prymitywów wprowadza nową klasę bazową zarówno dla Samplera, jak i Estimatora (BaseSamplerV2 i BaseEstimatorV2), a także nowe typy danych wejściowych i wyjściowych.

Nowy interfejs pozwala określić jeden Circuit i wiele obserwowalnych (w przypadku Estimatora) oraz zestawy wartości parametrów dla tego Circuit, dzięki czemu przeszukiwanie zestawów wartości parametrów i obserwowalnych może być efektywnie określone. Wcześniej trzeba było podawać ten sam Circuit wielokrotnie, by dopasować go do rozmiaru łączonych danych. Oprócz tego, choć nadal możesz korzystać z resilience_level (w przypadku Estimatora) jako prostego pokrętła, prymitywy V2 dają ci elastyczność włączania lub wyłączania poszczególnych metod ograniczania/tłumienia błędów, umożliwiając ich dostosowanie do swoich potrzeb.

Aby skrócić całkowity czas wykonania zadania, prymitywy V2 akceptują wyłącznie Circuit i obserwowalnych korzystające z instrukcji obsługiwanych przez docelową QPU (quantum processing unit). Takie Circuit i obserwowalnych określa się jako Circuit i obserwowalnych zgodnych z architekturą zestawu instrukcji (ISA). Prymitywy V2 nie wykonują operacji rozmieszczania, routingu ani translacji. Zapoznaj się z dokumentacją transpilacji, aby dowiedzieć się, jak przekształcać Circuit.

Sampler V2 został uproszczony, tak by skupiał się na swoim podstawowym zadaniu — próbkowaniu rejestru wyjściowego z wykonania Circuit kwantowych. Zwraca próbki, których typ jest zdefiniowany przez program, bez wag. Dane wyjściowe są też podzielone według nazw rejestrów wyjściowych zdefiniowanych przez program. Ta zmiana umożliwia przyszłą obsługę Circuit z klasycznym przepływem sterowania.

Pełne szczegóły znajdziesz w dokumentacji API EstimatorV2 oraz dokumentacji API SamplerV2.

Główne zmiany

Import

Ze względu na wsteczną kompatybilność musisz jawnie importować prymitywy V2. Użycie import <prymityw>V2 as <prymityw> nie jest wymagane, ale ułatwia przejście kodu na V2.

ostrzeżenie

Po usunięciu obsługi prymitywów V1 polecenie import <prymityw> będzie importować wersję V2 wskazanego prymitywu.

from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler

Wejście i wyjście

Wejście

Zarówno SamplerV2, jak i EstimatorV2 przyjmują jeden lub więcej prymitywnych bloków zunifikowanych (PUB) jako dane wejściowe. Każdy PUB to krotka zawierająca jeden Circuit i dane rozgłaszane do tego Circuit, czyli wiele obserwowalnych i parametrów. Każdy PUB zwraca jeden wynik.

  • Format PUB dla Sampler V2: (<circuit>, <wartości parametrów>, <shots>), gdzie <wartości parametrów> i <shots> są opcjonalne.
  • Format PUB dla Estimator V2: (<circuit>, <obserwowalne>, <wartości parametrów>, <precision>), gdzie <wartości parametrów> i <precision> są opcjonalne. Przy łączeniu obserwowalnych z wartościami parametrów stosowane są reguły rozgłaszania Numpy.

Dodatkowo wprowadzono następujące zmiany:

  • Estimator V2 zyskał argument precision w metodzie run(), który określa docelową precyzję szacowania wartości oczekiwanych.
  • Sampler V2 ma argument shots w metodzie run().
Przykłady

Przykład użycia Estimator V2 z precision w run():

# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

Przykład użycia Sampler V2 z shots w run():

# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)

# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])

Wyjście

Wyjście jest teraz w formacie PubResult. PubResult to dane i metadane będące wynikiem wykonania pojedynczego PUB.

  • Estimator V2 nadal zwraca wartości oczekiwane.

  • Część data wyniku PubResult dla Estimator V2 zawiera zarówno wartości oczekiwane, jak i błędy standardowe (stds). V1 zwracał wariancję w metadanych.

  • Sampler V2 zwraca pomiary dla każdego strzału w postaci ciągów bitów, zamiast rozkładów quasi-prawdopodobieństwa z interfejsu V1. Ciągi bitów pokazują wyniki pomiarów, zachowując kolejność strzałów, w jakiej zostały zmierzone.

  • Sampler V2 posiada wygodne metody, takie jak get_counts(), ułatwiające migrację.

  • Obiekty wynikowe Sampler V2 organizują dane według nazw klasycznych rejestrów Circuit wejściowych, dla kompatybilności z dynamicznymi Circuit. Domyślnie nazwa klasycznego rejestru to meas, jak pokazano w poniższym przykładzie. Jeśli podczas definiowania Circuit tworzysz jeden lub więcej klasycznych rejestrów z niestandardową nazwą, użyj tej nazwy, aby pobrać wyniki. Nazwę klasycznego rejestru możesz znaleźć, uruchamiając <nazwa_circuit>.cregs. Na przykład qc.cregs.

    # Define a quantum circuit with 2 qubits
    circuit = QuantumCircuit(2)
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure_all()
    circuit.draw()
            ┌───┐      ░ ┌─┐
    q_0: ┤ H ├──■───░─┤M├───
    └───┘┌─┴─┐ ░ └╥┘┌─┐
    q_1: ─────┤ X ├─░──╫─┤M├
    └───┘ ░ ║ └╥┘
    meas: 2/══════════════╩══╩═
    0 1

Przykłady dla Estimatora (wejście i wyjście)

# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values

# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs

Przykłady dla Samplera (wejście i wyjście)

  # Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists

# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()

Przykład użycia różnych rejestrów wyjściowych

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)

circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")

Opcje

Opcje są określane inaczej w prymitywach V2:

  • SamplerV2 i EstimatorV2 mają teraz oddzielne klasy opcji. Możesz przeglądać dostępne opcje i aktualizować ich wartości podczas inicjalizacji prymitywu lub po niej.
  • Zamiast metody set_options() opcje prymitywów V2 mają metodę update(), która stosuje zmiany do atrybutu options.
  • Jeśli nie podasz wartości dla opcji, przyjmuje ona specjalną wartość Unset i używane są domyślne wartości serwera.
  • Dla prymitywów V2 atrybut options jest typu dataclass w Pythonie. Możesz użyć wbudowanej metody asdict, aby przekonwertować go na słownik.

Listę dostępnych opcji znajdziesz w dokumentacji API.

from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})

# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})

# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True

# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))

Ograniczanie i tłumienie błędów

  • Ponieważ Sampler V2 zwraca próbki bez postprzetwarzania, nie obsługuje poziomów odporności.

  • Sampler V2 nie obsługuje optimization_level.

  • Estimator V2 przestanie obsługiwać optimization_level około 30 września 2024 roku.

  • Estimator V2 nie obsługuje poziomu odporności 3. Wynika to z faktu, że poziom 3 w V1 Estimatorze korzysta z Probabilistycznego Anulowania Błędów (PEC), które jest udowodnione jako dające nieobciążone wyniki kosztem wykładniczego czasu przetwarzania. Poziom 3 został usunięty, aby zwrócić uwagę na ten kompromis. Możesz jednak nadal korzystać z PEC jako metody ograniczania błędów, podając opcję pec_mitigation.

  • Estimator V2 obsługuje resilience_level 0–2 zgodnie z poniższą tabelą. Te opcje są bardziej zaawansowane niż ich odpowiedniki z V1. Możesz też jawnie włączać lub wyłączać poszczególne metody ograniczania/tłumienia błędów.

    Poziom 1Poziom 2
    Twirling pomiarówTwirling pomiarów
    Ograniczanie błędów odczytuOgraniczanie błędów odczytu
    ZNE
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend)

# Set resilience_level to 0
estimator.options.resilience_level = 0

# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")

Transpilacja

Prymitywy V2 obsługują wyłącznie Circuit zgodne z architekturą zestawu instrukcji (ISA) konkretnego Backend. Ponieważ prymitywy nie wykonują operacji rozmieszczania, routingu ani translacji, odpowiednie opcje transpilacji z V1 nie są obsługiwane.

Status zadania

Prymitywy V2 mają nową klasę RuntimeJobV2, która dziedziczy po BasePrimitiveJob. Metoda status() tej nowej klasy zwraca ciąg znaków zamiast enumeracji JobStatus z Qiskit. Szczegóły znajdziesz w dokumentacji API RuntimeJobV2.

job = estimator.run(...)

# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")

Kroki migracji do Estimator V2

  1. Zastąp from qiskit_ibm_runtime import Estimator przez from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. Usuń wszelkie instrukcje from qiskit_ibm_runtime import Options, ponieważ klasa Options nie jest używana przez prymitywy V2. Możesz zamiast tego przekazać opcje jako słownik podczas inicjalizacji klasy EstimatorV2 (np. estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), lub ustawić je po inicjalizacji:

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. Przejrzyj wszystkie obsługiwane opcje i wprowadź odpowiednie zmiany.

  4. Pogrupuj każdy Circuit, który chcesz uruchomić, z obserwoalnymi i wartościami parametrów, które chcesz do niego zastosować, w krotkę (PUB). Na przykład użyj (circuit1, observable1, parameter_set1), jeśli chcesz uruchomić circuit1 z observable1 i parameter_set1.

  5. Może być konieczne zmiana kształtu tablic obserwowalnych lub zestawów parametrów, jeśli chcesz zastosować ich iloczyn zewnętrzny. Na przykład tablica obserwowalnych o kształcie (4, 1) i tablica zestawów parametrów o kształcie (1, 6) dadzą wynik o kształcie (4, 6) wartości oczekiwanych. Więcej szczegółów znajdziesz w regułach rozgłaszania Numpy.

  6. Opcjonalnie możesz określić precyzję dla konkretnego PUB.

  7. Zaktualizuj metodę run() Estimatora, aby przekazać listę PUBów. Na przykład run([(circuit1, observable1, parameter_set1)]). Opcjonalnie możesz podać precision tutaj, co będzie miało zastosowanie do wszystkich PUBów.

  8. Wyniki zadania Estimator V2 są grupowane według PUBów. Możesz zobaczyć wartość oczekiwaną i błąd standardowy dla każdego PUB, indeksując do niego. Na przykład:

pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")

Pełne przykłady dla Estimatora

Uruchamianie pojedynczego eksperymentu

Użyj Estimatora, aby wyznaczyć wartość oczekiwaną pojedynczej pary Circuit–obserwowalne.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
estimator = Estimator(backend)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

Uruchamianie wielu eksperymentów w jednym zadaniu

Użyj Estimatora, aby wyznaczyć wartości oczekiwane wielu par Circuit–obserwowalne.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)

n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]

isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]

estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")

Uruchamianie sparametryzowanych Circuit

Użyj Estimatora, aby uruchomić wiele eksperymentów w jednym zadaniu, korzystając z wartości parametrów w celu zwiększenia możliwości ponownego użycia Circuit. W poniższym przykładzie zwróć uwagę, że kroki 1 i 2 są takie same dla V1 i V2.

import numpy as np

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem

theta = Parameter("θ")

chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)

number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]

ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]

from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Step 3: Execute using Qiskit primitives.

# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))

estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")

Korzystanie z sesji i zaawansowanych opcji

Odkryj sesje i zaawansowane opcje, aby zoptymalizować wydajność Circuit na QPU.

ostrożnie

Poniższy blok kodu zwróci błąd dla użytkowników planu Open Plan, ponieważ korzysta z sesji. Obciążenia robocze w planie Open Plan mogą być uruchamiane wyłącznie w trybie zadania lub trybie wsadowym.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
estimator = Estimator()

estimator.options.resilience_level = 1

job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

# second job
print(f" > Another Expectation value: {another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")

Kroki migracji do Sampler V2

  1. Zastąp from qiskit_ibm_runtime import Sampler przez from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. Usuń wszelkie instrukcje from qiskit_ibm_runtime import Options, ponieważ klasa Options nie jest używana przez prymitywy V2. Możesz zamiast tego przekazać opcje jako słownik podczas inicjalizacji klasy SamplerV2 (np. sampler = Sampler(backend, options={"default_shots": 1024})), lub ustawić je po inicjalizacji:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. Przejrzyj wszystkie obsługiwane opcje i wprowadź odpowiednie zmiany.
  4. Pogrupuj każdy Circuit, który chcesz uruchomić, z obserwoalnymi i wartościami parametrów, które chcesz do niego zastosować, w krotkę (PUB). Na przykład użyj (circuit1, parameter_set1), jeśli chcesz uruchomić circuit1 z parameter_set1. Opcjonalnie możesz określić liczbę strzałów dla konkretnego PUB.
  5. Zaktualizuj metodę run() Samplera, aby przekazać listę PUBów. Na przykład run([(circuit1, parameter_set1)]). Opcjonalnie możesz podać shots tutaj, co będzie miało zastosowanie do wszystkich PUBów.
  6. Wyniki zadania Sampler V2 są grupowane według PUBów. Możesz zobaczyć dane wyjściowe dla każdego PUB, indeksując do niego. Chociaż Sampler V2 zwraca nieważone próbki, klasa wynikowa ma wygodną metodę do uzyskiwania zliczeń. Na przykład:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
uwaga

Aby uzyskać wyniki, potrzebujesz nazwy klasycznego rejestru. Domyślnie nosi ona nazwę meas, gdy używasz measure_all(). Jeśli podczas definiowania Circuit tworzysz jeden lub więcej klasycznych rejestrów z niestandardową nazwą, użyj tej nazwy, aby pobrać wyniki. Nazwę klasycznego rejestru możesz znaleźć, uruchamiając <nazwa_circuit>.cregs. Na przykład qc.cregs.

Pełne przykłady dla Samplera

Uruchamianie pojedynczego eksperymentu

Użyj Samplera, aby wyznaczyć zliczenia lub rozkład quasi-prawdopodobieństwa dla pojedynczego Circuit.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()

Uruchamianie wielu eksperymentów w jednym zadaniu

Użyj Samplera, aby wyznaczyć zliczenia lub rozkłady quasi-prawdopodobieństwa dla wielu Circuit w jednym zadaniu.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)

sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()

for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")

Uruchamianie sparametryzowanych Circuit

Uruchom kilka eksperymentów w jednym zadaniu, korzystając z wartości parametrów w celu zwiększenia możliwości ponownego użycia Circuit.

import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()

# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# Step 3: Execute using Qiskit primitives.

from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")

Korzystanie z sesji i zaawansowanych opcji

Odkryj sesje i zaawansowane opcje, aby zoptymalizować wydajność Circuit na QPU.

ostrożnie

Poniższy blok kodu zwróci błąd dla użytkowników planu Open Plan, ponieważ korzysta z sesji. Obciążenia robocze w planie Open Plan mogą być uruchamiane wyłącznie w trybie zadania lub trybie wsadowym.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)

service = QiskitRuntimeService()

# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")

# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")

Kolejne kroki

Zalecenia