Migracja do prymitywów Qiskit Runtime V2
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.
Po usunięciu obsługi prymitywów V1 polecenie import <prymityw> będzie importować wersję V2 wskazanego prymitywu.
- Estimator V2
- Estimator (V1)
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Estimator
- Sampler V2
- Sampler (V1)
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import 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
precisionw metodzierun(), który określa docelową precyzję szacowania wartości oczekiwanych. - Sampler V2 ma argument
shotsw metodzierun().
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ęść
datawyniku 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ładqc.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)
- 1 circuit, 4 obserwowalne
- 1 circuit, 4 obserwowalne, 2 zestawy parametrów
- 2 circuity, 2 obserwowalne
# 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
# Estimator V1: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v1.run([circuit] * 8, [obs1, obs2, obs3, obs4] * 2, [vals1, vals2] * 4)
evs = job.result().values
# Estimator V2: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v2.run([(circuit, [[obs1], [obs2], [obs3], [obs4]], [[vals1], [vals2]])])
evs = job.result()[0].data.evs
# Estimator V1: Cannot execute 2 circuits with different observables
# Estimator V2: Execute 2 circuits with 2 different observables. There are
# two PUBs because each PUB can have only one circuit.
job = estimator_v2.run([(circuit1, obs1), (circuit2, obs2)])
evs1 = job.result()[0].data.evs # result for pub 1 (circuit 1)
evs2 = job.result()[1].data.evs # result for pub 2 (circuit 2)
Przykłady dla Samplera (wejście i wyjście)
- 1 circuit, 3 zestawy parametrów
- 2 circuity, 1 zestaw parametrów
- Konwersja wyjścia V2 do formatu V1
# 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()
# Sampler V1: Execute 2 circuits with 1 parameter set
job = sampler_v1.run([circuit1, circuit2], [vals1] * 2)
dists = job.result().quasi_dists
# Sampler V2: Execute 2 circuits with 1 parameter set
job = sampler_v2.run([(circuit1, vals1), (circuit2, vals1)])
counts1 = job.result()[0].data.meas.get_counts() # result for pub 1 (circuit 1)
counts2 = job.result()[1].data.meas.get_counts() # result for pub 2 (circuit 2)
Format wyjściowy V1 to słownik, w którym kluczem jest ciąg bitów (jako liczba całkowita), a wartością — quasi-prawdopodobieństwo dla każdego Circuit. Format V2 ma ten sam klucz (ale jako ciąg znaków) i zliczenia jako wartość. Aby przekonwertować format V2 na V1, podziel zliczenia przez liczbę strzałów; sposób wyboru liczby strzałów opisano w przewodniku Określanie opcji.
v2_result = sampler_v2_job.result()
v1_format = []
for pub_result in v2_result:
counts = pub_result.data.meas.get_counts()
v1_format.append( {int(key, 2): val/shots for key, val in counts.items()} )
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:
SamplerV2iEstimatorV2mają 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 atrybutuoptions. - Jeśli nie podasz wartości dla opcji, przyjmuje ona specjalną wartość
Unseti używane są domyślne wartości serwera. - Dla prymitywów V2 atrybut
optionsjest typudataclassw Pythonie. Możesz użyć wbudowanej metodyasdict, aby przekonwertować go na słownik.
Listę dostępnych opcji znajdziesz w dokumentacji API.
- Estimator V2
- Estimator (V1)
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 qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
estimator = Estimator(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
estimator.set_options(shots=4000)
- Sampler V2
- Sampler (V1)
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))
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
sampler = Sampler(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
sampler.set_options(shots=2000)
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_levelokoł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_level0–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 1 Poziom 2 Twirling pomiarów Twirling pomiarów Ograniczanie błędów odczytu Ograniczanie błędów odczytu ZNE
- Estimator V2
- Estimator (V1)
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 Estimator, Options
estimator = Estimator(backend, options=options)
options = Options()
options.resilience_level = 2
- Sampler V2
- Sampler (V1)
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}")
from qiskit_ibm_runtime import Sampler, Options
sampler = Sampler(backend, options=options)
options = Options()
options.resilience_level = 2
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.
- Prymitywy V2
- Prymitywy V1
job = estimator.run(...)
# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")
from qiskit.providers.jobstatus import JobStatus
job = estimator.run(...)
#check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() is JobStatus.RUNNING}")
Kroki migracji do Estimator V2
-
Zastąp
from qiskit_ibm_runtime import Estimatorprzezfrom qiskit_ibm_runtime import EstimatorV2 as Estimator. -
Usuń wszelkie instrukcje
from qiskit_ibm_runtime import Options, ponieważ klasaOptionsnie jest używana przez prymitywy V2. Możesz zamiast tego przekazać opcje jako słownik podczas inicjalizacji klasyEstimatorV2(np.estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), lub ustawić je po inicjalizacji:estimator = Estimator(backend)
estimator.options.dynamical_decoupling.enable = True -
Przejrzyj wszystkie obsługiwane opcje i wprowadź odpowiednie zmiany.
-
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ćcircuit1zobservable1iparameter_set1. -
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.
-
Opcjonalnie możesz określić precyzję dla konkretnego PUB.
-
Zaktualizuj metodę
run()Estimatora, aby przekazać listę PUBów. Na przykładrun([(circuit1, observable1, parameter_set1)]). Opcjonalnie możesz podaćprecisiontutaj, co będzie miało zastosowanie do wszystkich PUBów. -
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.
- Estimator V2
- Estimator (V1)
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}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
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)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
estimator = Estimator(backend)
job = estimator.run(isa_circuit, isa_observable)
result = job.result()
print(f" > Observable: {observable.paulis}")
print(f" > Expectation value: {result.values}")
print(f" > Metadata: {result.metadata}")
Uruchamianie wielu eksperymentów w jednym zadaniu
Użyj Estimatora, aby wyznaczyć wartości oczekiwane wielu par Circuit–obserwowalne.
- Estimator V2
- Estimator (V1)
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}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
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]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]
# Get ISA circuits
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
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, isa_observables)
result = job.result()
print(f" > Expectation values: {result.values}")
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.
- Estimator V2
- Estimator (V1)
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}")
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 Estimator
# Step 3: Execute using Qiskit Primitives.
num_ops = len(isa_observables)
batch_circuits = [chsh_isa_circuit] * number_of_phases * num_ops
batch_ops = [op for op in isa_observables for _ in individual_phases]
batch_phases = individual_phases * num_ops
estimator = Estimator(backend, options={"shots": int(1e4)})
job = estimator.run(batch_circuits, batch_ops, batch_phases)
expvals = job.result().values
Korzystanie z sesji i zaawansowanych opcji
Odkryj sesje i zaawansowane opcje, aby zoptymalizować wydajność Circuit na QPU.
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.
- Estimator V2
- Estimator (V1)
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}")
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, Estimator, Options
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(backend=backend, optimization_level=1)
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)
options = Options()
options.optimization_level = 2
options.resilience_level = 2
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
estimator = Estimator(options=options)
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 values job 1: {result.values}")
# second job
print(f" > Expectation values job 2: {another_result.values}")
Kroki migracji do Sampler V2
- Zastąp
from qiskit_ibm_runtime import Samplerprzezfrom qiskit_ibm_runtime import SamplerV2 as Sampler. - Usuń wszelkie instrukcje
from qiskit_ibm_runtime import Options, ponieważ klasaOptionsnie jest używana przez prymitywy V2. Możesz zamiast tego przekazać opcje jako słownik podczas inicjalizacji klasySamplerV2(np.sampler = Sampler(backend, options={"default_shots": 1024})), lub ustawić je po inicjalizacji:sampler = Sampler(backend)
sampler.options.default_shots = 1024 - Przejrzyj wszystkie obsługiwane opcje i wprowadź odpowiednie zmiany.
- 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ćcircuit1zparameter_set1. Opcjonalnie możesz określić liczbę strzałów dla konkretnego PUB. - Zaktualizuj metodę
run()Samplera, aby przekazać listę PUBów. Na przykładrun([(circuit1, parameter_set1)]). Opcjonalnie możesz podaćshotstutaj, co będzie miało zastosowanie do wszystkich PUBów. - 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()}")
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.
- Sampler V2
- Sampler (V1)
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()
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
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()
sampler = Sampler(backend)
job = sampler.run(circuit)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
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.
- Sampler V2
- Sampler (V1)
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()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, 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()
sampler = Sampler(backend)
job = sampler.run(circuits)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
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.
- Sampler V2
- Sampler (V1)
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()}")
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 = 5
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 Sampler
sampler = Sampler(backend)
job = sampler.run([isa_circuit] * 3, parameter_values)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
Korzystanie z sesji i zaawansowanych opcji
Odkryj sesje i zaawansowane opcje, aby zoptymalizować wydajność Circuit na QPU.
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.
- Sampler V2
- Sampler (V1)
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()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session, Options
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()
options = Options()
options.optimization_level = 2
options.resilience_level = 0
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
sampler = Sampler(options=options)
job = sampler.run(circuit)
another_job = sampler.run(another_circuit)
result = job.result()
another_result = another_job.result()
# first job
print(f" > Quasi-probability distribution job 1: {result.quasi_dists}")
# second job
print(f" > Quasi-probability distribution job 2: {another_result.quasi_dists}")
Kolejne kroki
- Dowiedz się więcej o ustawianiu opcji w przewodniku Określanie opcji.
- Poznaj więcej szczegółów na temat danych wejściowych i wyjściowych prymitywów.
- Eksperymentuj z samouczkiem Nierówność CHSH.