Migrace na primitiva V2 Qiskit Runtime
Původní primitiva (označovaná jako primitiva V1), V1 Sampler a V1 Estimator, byla v qiskit-ibm-runtime 0.23 označena jako zastaralá.
Jejich podpora byla ukončena 15. srpna 2024.
V důsledku zastarání primitiv V1 by měl být veškerý kód migrován na rozhraní V2. Tento průvodce popisuje, co se změnilo v primitivech V2 Qiskit Runtime (dostupných od qiskit-ibm-runtime 0.21.0) a proč, podrobně popisuje každé nové primitivum a uvádí příklady, které ti pomohou migrovat kód z původních primitiv na primitiva V2. Příklady v průvodci všechny používají primitiva Qiskit Runtime, ale obecně platí stejné změny i pro ostatní implementace primitiv. Funkce jedinečné pro Qiskit Runtime, jako je potlačení chyb, zůstávají jedinečné pro Qiskit Runtime.
Informace o změnách v referenčních primitivech Qiskit (nyní nazývaných statevector primitiva) najdeš v sekci qiskit.primitives na stránce změn Qiskit 1.0. Referenční implementace primitiv V2 jsou StatevectorSampler a StatevectorEstimator.
Přehled
Verze 2 primitiv je zavedena s novou základní třídou pro Sampler i Estimator (BaseSamplerV2 a BaseEstimatorV2) spolu s novými typy pro jejich vstupy a výstupy.
Nové rozhraní ti umožňuje zadat jeden Circuit a více observablů (při použití Estimatoru) a sady hodnot parametrů pro daný Circuit, takže průchody přes sady hodnot parametrů a observably lze efektivně specifikovat. Dříve bylo nutné stejný Circuit zadávat vícekrát, aby odpovídal velikosti kombinovaných dat. Navíc, přestože stále můžeš používat resilience_level (při použití Estimatoru) jako jednoduchý přepínač, primitiva V2 ti dávají flexibilitu zapínat nebo vypínat jednotlivé metody potlačení nebo zmírnění chyb a přizpůsobit je svým potřebám.
Za účelem zkrácení celkové doby provádění úloh přijímají primitiva V2 pouze Circuit a observably, které používají instrukce podporované cílovým QPU (kvantovou procesorovou jednotkou). Takové Circuit a observably se označují jako ISA (instruction set architecture) Circuit a observably. Primitiva V2 neprovádějí operace rozvržení, směrování a překladu. Pokyny k transformaci Circuit najdeš v dokumentaci k transpilaci.
Sampler V2 je zjednodušen tak, aby se zaměřil na svůj hlavní úkol: vzorkování výstupního registru při spuštění kvantových Circuit. Vrací vzorky, jejichž typ je definován programem, bez vah. Výstupní data jsou také rozdělena podle názvů výstupních registrů definovaných programem. Tato změna umožňuje budoucí podporu Circuit s klasickým řízením toku.
Úplné podrobnosti najdeš v referenci API EstimatorV2 a referenci API SamplerV2.
Hlavní změny
Import
Z důvodu zpětné kompatibility musíš primitiva V2 explicitně importovat. Zápis import <primitive>V2 as <primitive> není povinný, ale usnadňuje přechod kódu na V2.
Poté, co primitiva V1 přestanou být podporována, příkaz import <primitive> importuje verzi V2 daného primitivu.
- 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
Vstup a výstup
Vstup
SamplerV2 i EstimatorV2 přijímají jako vstup jeden nebo více primitive unified blocs (PUB). Každý PUB je n-tice obsahující jeden Circuit a data vysílaná do tohoto Circuit, což může být více observabilů a parametrů. Každý PUB vrátí výsledek.
- Formát PUB pro Sampler V2: (
<circuit>,<hodnoty parametrů>,<shots>), kde<hodnoty parametrů>a<shots>jsou volitelné. - Formát PUB pro Estimator V2: (
<circuit>,<observables>,<hodnoty parametrů>,<precision>), kde<hodnoty parametrů>a<precision>jsou volitelné. Při kombinování observabilů a hodnot parametrů se používají pravidla broadcastingu z knihovny Numpy.
Dále byly provedeny následující změny:
- Estimator V2 získal argument
precisionv metoděrun(), který určuje cílovou přesnost odhadů střední hodnoty. - Sampler V2 má argument
shotsve své metoděrun().
Příklady
Příklad Estimator V2 používající precision v run():
# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
Příklad Sampler V2 používající shots v 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),
])
Výstup
Výstup je nyní ve formátu PubResult. PubResult obsahuje data a metadata vzniklá spuštěním jednoho PUB.
-
Estimator V2 nadále vrací střední hodnoty.
-
Část
datavýsledku PubResult pro Estimator V2 obsahuje jak střední hodnoty, tak standardní chyby (stds). V1 vracela rozptyl v metadatech. -
Sampler V2 vrací měření pro každý shot ve formě bitstring, místo kvazi-pravděpodobnostních distribucí z rozhraní V1. Bitstring zobrazují výsledky měření a zachovávají pořadí shotů, ve kterém byly změřeny.
-
Sampler V2 obsahuje pomocné metody jako
get_counts(), které usnadňují migraci. -
Výsledné objekty Sampler V2 organizují data podle názvů klasických registrů vstupních Circuit, kvůli kompatibilitě s dynamickými Circuit. Ve výchozím nastavení je název klasického registru
meas, jak ukazuje následující příklad. Pokud při definování svého Circuit vytvoříš jeden nebo více klasických registrů s jiným než výchozím názvem, použij tento název pro získání výsledků. Název klasického registru zjistíš spuštěním<název_circuit>.cregs. Napříkladqc.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
Příklady Estimatoru (vstup a výstup)
- 1 Circuit, 4 observables
- 1 Circuit, 4 observables, 2 sady parametrů
- 2 Circuits, 2 observables
# 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)
Příklady Sampleru (vstup a výstup)
- 1 Circuit, 3 sady parametrů
- 2 Circuits, 1 sada parametrů
- Převod výstupu V2 do formátu 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)
Výstupní formát V1 byl slovník, kde klíčem byl bitový řetězec (jako celé číslo) a hodnotou kvazi-pravděpodobnosti pro každý Circuit. Formát V2 má stejný klíč (ale jako řetězec) a jako hodnotu počty. Abys převedl formát V2 do V1, vydělíš počty počtem měření (shots), přičemž volba počtu shots je popsána v průvodci Zadání možností.
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()} )
Příklad s různými výstupními registry
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()}")
Options
Možnosti jsou v primitívech V2 zadávány odlišným způsobem:
SamplerV2aEstimatorV2mají nyní samostatné třídy možností. Dostupné možnosti si můžeš prohlédnout a hodnoty aktualizovat během inicializace primitívu nebo po ní.- Místo metody
set_options()mají možnosti primitívů V2 metoduupdate(), která aplikuje změny na atributoptions. - Pokud nezadáš hodnotu pro některou možnost, dostane speciální hodnotu
Unseta použijí se výchozí hodnoty serveru. - U primitívů V2 je atribut
optionsdatový typdataclassv Pythonu. Můžeš použít vestavěnou metoduasdictpro převod na slovník.
Seznam dostupných možností najdeš v referenci 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)
Zmírnění a potlačení chyb
-
Protože Sampler V2 vrací vzorky bez následného zpracování, nepodporuje úrovně odolnosti.
-
Sampler V2 nepodporuje
optimization_level. -
Estimator V2 přestane podporovat
optimization_levelpřibližně 30. září 2024. -
Estimator V2 nepodporuje úroveň odolnosti 3. Důvodem je, že úroveň odolnosti 3 v Estimatoru V1 využívá metodu Probabilistic Error Cancellation (PEC), která je prokázaně schopna poskytovat nezkreslené výsledky za cenu exponenciální doby zpracování. Úroveň 3 byla odstraněna, aby upozornila na tento kompromis. Metodu PEC ale stále můžeš používat jako metodu zmírnění chyb tím, že zadáš možnost
pec_mitigation. -
Estimator V2 podporuje
resilience_level0–2, jak popisuje následující tabulka. Tyto možnosti jsou pokročilejší než jejich protějšky ve V1. Jednotlivé metody zmírnění / potlačení chyb lze také explicitně zapnout nebo vypnout.Úroveň 1 Úroveň 2 Measurement twirling Measurement twirling Zmírnění chyb čtení Zmírnění chyb čtení 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
Transpilace
Primitiva V2 podporují pouze obvody (Circuit), které odpovídají instrukční sadě architektury (ISA) daného Backend. Protože primitiva neprovádějí operace rozmístění, směrování a překladu, nejsou příslušné možnosti transpilace z V1 podporovány.
Stav jobu
Primitiva V2 mají novou třídu RuntimeJobV2, která dědí z BasePrimitiveJob. Metoda status() této nové třídy vrací řetězec namísto výčtu JobStatus z Qiskitu. Podrobnosti najdeš v referenci API pro RuntimeJobV2.
- V2 primitives
- V1 primitives
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}")
Kroky pro migraci na Estimator V2
-
Nahraď
from qiskit_ibm_runtime import Estimatorzafrom qiskit_ibm_runtime import EstimatorV2 as Estimator. -
Odstraň všechny příkazy
from qiskit_ibm_runtime import Options, protože třídaOptionsnení primitivy V2 používána. Místo toho můžeš předávat volby jako slovník při inicializaci třídyEstimatorV2(napříkladestimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), nebo je nastavit až po inicializaci:estimator = Estimator(backend)
estimator.options.dynamical_decoupling.enable = True -
Projdi všechny podporované volby a podle potřeby je aktualizuj.
-
Seskup každý Circuit, který chceš spustit, spolu s observablemi a hodnotami parametrů, jež chceš na Circuit aplikovat, do n-tice (PUB). Například použij
(circuit1, observable1, parameter_set1), pokud chceš spustitcircuit1sobservable1aparameter_set1. -
Možná budeš muset změnit tvar svých polí observablů nebo sad parametrů, pokud chceš aplikovat jejich vnější součin. Například pole observablů tvaru (4, 1) a pole sad parametrů tvaru (1, 6) ti dají výsledek (4, 6) očekávaných hodnot. Podrobnosti najdeš v pravidlech broadcastingu Numpy.
-
Pro daný konkrétní PUB můžeš volitelně určit požadovanou přesnost.
-
Aktualizuj metodu
run()Estimatoru tak, aby přijímala seznam PUBů. Napříkladrun([(circuit1, observable1, parameter_set1)]). Volitelně zde můžeš zadatprecision, která se aplikuje na všechny PUBy. -
Výsledky úlohy Estimatoru V2 jsou seskupeny podle PUBů. Očekávanou hodnotu a standardní chybu pro každý PUB zobrazíš indexováním. Například:
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
Úplné příklady pro Estimator
Spuštění jediného experimentu
Pomocí Estimatoru urči očekávanou hodnotu jediného páru Circuit–observable.
- 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}")
Spuštění více experimentů v rámci jedné úlohy
Pomocí Estimatoru zjisti očekávané hodnoty více dvojic okruh–pozorovatelná.
- 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}")
Spuštění parametrizovaných Circuit
Použij Estimator ke spuštění více experimentů v rámci jedné úlohy, přičemž hodnoty parametrů zvyšují znovupoužitelnost Circuit. V následujícím příkladu si všimni, že kroky 1 a 2 jsou stejné pro 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
Použití Session a pokročilých možností
Prozkoumej Session a pokročilé možnosti pro optimalizaci výkonu Circuit na QPU.
Následující blok kódu vrátí chybu uživatelům na Open Plánu, protože využívá Session. Úlohy na Open Plánu mohou běžet pouze v job mode nebo batch mode.
- 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}")
Postup migrace na Sampler V2
- Nahraď
from qiskit_ibm_runtime import Samplerzafrom qiskit_ibm_runtime import SamplerV2 as Sampler. - Odstraň veškeré příkazy
from qiskit_ibm_runtime import Options, protože třídaOptionsnení v primitivech V2 používána. Místo toho můžeš předat volby jako slovník při inicializaci třídySamplerV2(napříkladsampler = Sampler(backend, options={"default_shots": 1024})), nebo je nastavit po inicializaci:sampler = Sampler(backend)
sampler.options.default_shots = 1024 - Projdi si všechny podporované volby a proveď příslušné úpravy.
- Seskup každý Circuit, který chceš spustit, s observablemi a hodnotami parametrů, které chceš na Circuit použít, do n-tice (PUB). Například použij
(circuit1, parameter_set1), pokud chceš spustitcircuit1sparameter_set1. Pro daný konkrétní PUB můžeš volitelně zadat počet shots. - Aktualizuj metodu
run()Sampleru tak, aby přijímala seznam PUBů. Napříkladrun([(circuit1, parameter_set1)]). Volitelně zde můžeš zadatshots, které se uplatní na všechny PUBů. - Výsledky jobu Sampleru V2 jsou seskupeny podle PUBů. Výstupní data pro každý PUB zobrazíš indexováním na příslušný PUB. Zatímco Sampler V2 vrací nevážené vzorky, třída výsledků obsahuje pohodlnou metodu pro získání počtů. Například:
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()}")
Pro získání výsledků potřebuješ název klasického registru. Ve výchozím nastavení se jmenuje meas, pokud použiješ measure_all(). Pokud při definování Circuitu vytvoříš jeden nebo více klasických registrů s jiným než výchozím názvem, použij k získání výsledků tento název. Název klasického registru zjistíš spuštěním <circuit_name>.cregs. Například qc.cregs.
Úplné příklady použití Sampleru
Spuštění jediného experimentu
Pomocí Sampleru zjistíš počty nebo kvázi-pravděpodobnostní rozdělení pro jediný 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}")