Migrează la primitivele Qiskit Runtime V2
Primitivele originale (denumite primitive V1), V1 Sampler și V1 Estimator, au fost deprecate în qiskit-ibm-runtime 0.23.
Suportul lor a fost eliminat pe 15 august 2024.
Odată cu deprecierea primitivelor V1, tot codul ar trebui migrat pentru a utiliza interfețele V2. Acest ghid descrie ce s-a schimbat în primitivele Qiskit Runtime V2 (disponibile cu qiskit-ibm-runtime 0.21.0) și motivele acestor schimbări, descrie în detaliu fiecare primitivă nouă și oferă exemple care te ajută să migrezi codul de la primitivele moștenite la primitivele V2. Exemplele din ghid utilizează toate primitivele Qiskit Runtime, dar, în general, aceleași modificări se aplică și celorlalte implementări de primitive. Funcțiile unice ale Qiskit Runtime, cum ar fi atenuarea erorilor, rămân specifice Qiskit Runtime.
Pentru informații despre modificările aduse primitivelor de referință Qiskit (acum numite primitive statevector), consultă secțiunea qiskit.primitives din pagina de modificări ale funcționalităților Qiskit 1.0. Consultă StatevectorSampler și StatevectorEstimator pentru implementările de referință ale primitivelor V2.
Prezentare generală
Versiunea 2 a primitivelor este introdusă cu o nouă clasă de bază atât pentru Sampler, cât și pentru Estimator (BaseSamplerV2 și BaseEstimatorV2), împreună cu noi tipuri pentru intrările și ieșirile acestora.
Noua interfață îți permite să specifici un singur Circuit și mai multe observabile (dacă folosești Estimator) și seturi de valori de parametri pentru acel Circuit, astfel încât parcurgerea seturilor de valori de parametri și a observabilelor poate fi specificată eficient. Anterior, trebuia să specifici același Circuit de mai multe ori pentru a corespunde dimensiunii datelor ce urmau să fie combinate. De asemenea, deși poți în continuare să folosești resilience_level (dacă folosești Estimator) ca buton simplu de reglaj, primitivele V2 îți oferă flexibilitatea de a activa sau dezactiva individual metodele de atenuare/suprimare a erorilor, pentru a le personaliza în funcție de nevoile tale.
Pentru a reduce timpul total de execuție a job-ului, primitivele V2 acceptă doar Circuite și observabile care utilizează instrucțiuni suportate de QPU-ul țintă (unitate de procesare cuantică). Astfel de Circuite și observabile sunt denumite Circuite și observabile cu arhitectură set de instrucțiuni (ISA). Primitivele V2 nu efectuează operații de layout, rutare și traducere. Consultă documentația de transpilare pentru instrucțiuni de transformare a Circuitelor.
Sampler V2 este simplificat pentru a se concentra pe sarcina sa de bază de eșantionare a registrului de ieșire din execuția Circuitelor cuantice. Returnează eșantioanele, al căror tip este definit de program, fără ponderi. Datele de ieșire sunt, de asemenea, separate în funcție de numele registrelor de ieșire definite de program. Această modificare permite suportul viitor pentru Circuite cu flux de control clasic.
Consultă referința API EstimatorV2 și referința API SamplerV2 pentru detalii complete.
Modificări majore
Import
Din motive de compatibilitate retroactivă, trebuie să imporți explicit primitivele V2. Specificarea import <primitive>V2 as <primitive> nu este obligatorie, dar face mai ușoară tranziția codului la V2.
După ce primitivele V1 nu mai sunt suportate, import <primitive> va importa versiunea V2 a primitivei specificate.
- 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
Intrare și ieșire
Intrare
Atât SamplerV2, cât și EstimatorV2 primesc unul sau mai multe blocuri unificate primitive (PUB) ca intrare. Fiecare PUB este un tuplu care conține un singur Circuit și datele difuzate către acel Circuit, care pot fi mai multe observabile și parametri. Fiecare PUB returnează un rezultat.
- Formatul PUB pentru Sampler V2: (
<circuit>,<parameter values>,<shots>), unde<parameter values>și<shots>sunt opționale. - Formatul PUB pentru Estimator V2: (
<circuit>,<observables>,<parameter values>,<precision>), unde<parameter values>și<precision>sunt opționale. Regulile de difuzare Numpy sunt utilizate la combinarea observabilelor și a valorilor de parametri.
În plus, au fost efectuate următoarele modificări:
- Estimator V2 a câștigat un argument
precisionîn metodarun(), care specifică precizia țintită a estimărilor valorilor de așteptare. - Sampler V2 are argumentul
shotsîn metoda sarun().
Exemple
Exemplu Estimator V2 care utilizează precision în run():
# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
Exemplu Sampler V2 care utilizează shots în 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),
])
Ieșire
Ieșirea este acum în formatul PubResult. Un PubResult reprezintă datele și metadatele rezultate din execuția unui singur PUB.
-
Estimator V2 continuă să returneze valori de așteptare.
-
Partea
dataa unui PubResult Estimator V2 conține atât valorile de așteptare, cât și erorile standard (stds). V1 returna varianța în metadate. -
Sampler V2 returnează măsurători per-shot sub formă de șiruri de biți, în loc de distribuțiile quasi-probabilistice din interfața V1. Șirurile de biți arată rezultatele măsurătorilor, păstrând ordinea shot-urilor în care au fost măsurate.
-
Sampler V2 are metode utilitare precum
get_counts()pentru a facilita migrarea. -
Obiectele de rezultate ale Sampler V2 organizează datele în funcție de numele registrelor clasice ale Circuitelor de intrare, pentru compatibilitate cu Circuitele dinamice. În mod implicit, numele registrului clasic este
meas, după cum se arată în exemplul următor. Când îți definești Circuitul, dacă creezi unul sau mai multe registre clasice cu un nume non-implicit, folosește acel nume pentru a obține rezultatele. Poți găsi numele registrului clasic executând<circuit_name>.cregs. De exemplu,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
Exemple Estimator (intrare și ieșire)
- 1 circuit, 4 observables
- 1 circuit, 4 observables, 2 parameter sets
- 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)
Exemple Sampler (intrare și ieșire)
- 1 circuit, 3 seturi de parametri
- 2 circuite, 1 set de parametri
- Conversie ieșire V2 în format 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)
Formatul de ieșire V1 era un dicționar în care cheile erau șiruri de biți (ca număr întreg), iar valorile erau cvasi-probabilități pentru fiecare circuit. Formatul V2 are aceeași cheie (dar ca șir de caractere) și numărătorile ca valori. Pentru a converti formatul V2 în V1, împarte numărătorile la numărul de cadre (shots), unde numărul de cadre selectat este descris în ghidul Specificarea opțiunilor.
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()} )
Exemplu care utilizează registre de ieșire diferite
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
Opțiunile sunt specificate diferit în primitivele V2, în următoarele moduri:
SamplerV2șiEstimatorV2au acum clase de opțiuni separate. Poți vedea opțiunile disponibile și poți actualiza valorile opțiunilor în timpul sau după inițializarea primitivei.- În loc de metoda
set_options(), opțiunile primitivelor V2 dispun de metodaupdate(), care aplică modificările atributuluioptions. - Dacă nu specifici o valoare pentru o opțiune, aceasta primește valoarea specială
Unsetși se folosesc valorile implicite ale serverului. - Pentru primitivele V2, atributul
optionseste de tipul Pythondataclass. Poți folosi metoda built-inasdictpentru a-l converti într-un dicționar.
Consultă referința API pentru lista opțiunilor disponibile.
- 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)
Atenuarea și suprimarea erorilor
-
Deoarece Sampler V2 returnează eșantioane fără postprocesare, nu acceptă niveluri de reziliență.
-
Sampler V2 nu acceptă
optimization_level. -
Estimator V2 va renunța la suportul pentru
optimization_levelîn jurul datei de 30 septembrie 2024. -
Estimator V2 nu acceptă nivelul de reziliență 3. Acest lucru se datorează faptului că nivelul 3 în Estimator V1 folosește Probabilistic Error Cancellation (PEC), despre care s-a demonstrat că oferă rezultate nebiasate cu costul unui timp de procesare exponențial. Nivelul 3 a fost eliminat pentru a atrage atenția asupra acestui compromis. Poți folosi în continuare PEC ca metodă de atenuare a erorilor, specificând opțiunea
pec_mitigation. -
Estimator V2 acceptă
resilience_level0-2, conform tabelului următor. Aceste opțiuni sunt mai avansate decât echivalentele lor din V1. De asemenea, poți activa/dezactiva explicit metode individuale de atenuare/suprimare a erorilor.Nivel 1 Nivel 2 Measurement twirling Measurement twirling Readout error mitigation Readout error mitigation 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
Transpilare
Primitivele V2 acceptă doar circuite care respectă Instruction Set Architecture (ISA) a unui backend specific. Deoarece primitivele nu efectuează operații de layout, rutare și traducere, opțiunile de transpilare corespunzătoare din V1 nu sunt acceptate.
Starea job-ului
Primitivele V2 au o nouă clasă RuntimeJobV2, care moștenește din BasePrimitiveJob. Metoda status() a acestei noi clase returnează un șir de caractere în loc de o enumerare JobStatus din Qiskit. Consultă referința API RuntimeJobV2 pentru detalii.
- 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}")
Pași pentru migrarea la Estimator V2
-
Înlocuiește
from qiskit_ibm_runtime import Estimatorcufrom qiskit_ibm_runtime import EstimatorV2 as Estimator. -
Elimină toate instrucțiunile
from qiskit_ibm_runtime import Options, deoarece clasaOptionsnu este folosită de primitivele V2. În schimb, poți transmite opțiunile ca un dicționar la inițializarea claseiEstimatorV2(de exemplu,estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), sau le poți seta după inițializare:estimator = Estimator(backend)
estimator.options.dynamical_decoupling.enable = True -
Revizuiește toate opțiunile acceptate și efectuează actualizările necesare.
-
Grupează fiecare circuit pe care vrei să-l rulezi împreună cu observabilele și valorile parametrilor pe care vrei să le aplici circuitului, într-un tuplu (un PUB). De exemplu, folosește
(circuit1, observable1, parameter_set1)dacă vrei să rulezicircuit1cuobservable1șiparameter_set1. -
S-ar putea să fie nevoie să remodelezi tablourile de observabile sau de seturi de parametri dacă vrei să aplici produsul lor exterior. De exemplu, un tablou de observabile de forma (4, 1) și un tablou de seturi de parametri de forma (1, 6) îți vor oferi un rezultat de (4, 6) valori așteptate. Consultă regulile de broadcasting Numpy pentru mai multe detalii.
-
Poți specifica opțional precizia dorită pentru acel PUB specific.
-
Actualizează metoda
run()a estimatorului pentru a transmite lista de PUB-uri. De exemplu,run([(circuit1, observable1, parameter_set1)]). Poți specifica opțional o valoareprecisionaici, care s-ar aplica tuturor PUB-urilor. -
Rezultatele job-urilor Estimator V2 sunt grupate pe PUB-uri. Poți vedea valoarea așteptată și eroarea standard pentru fiecare PUB indexând la acesta. De exemplu:
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
Exemple complete cu Estimator
Rulează un singur experiment
Folosește Estimator pentru a determina valoarea de așteptare a unei singure perechi circuit-observabil.
- 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}")
Rulează mai multe experimente într-un singur job
Folosește Estimator pentru a determina valorile de așteptare ale mai multor perechi circuit-observabil.
- 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}")
Rularea circuitelor parametrizate
Folosește Estimator pentru a rula mai multe experimente într-un singur job, valorificând valorile parametrilor pentru a crește reutilizabilitatea Circuit-ului. În exemplul următor, observă că pașii 1 și 2 sunt identici pentru 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
Folosește sesiuni și opțiuni avansate
Explorează sesiunile și opțiunile avansate pentru a optimiza performanța circuitelor pe QPU-uri.
Blocul de cod următor va returna o eroare pentru utilizatorii cu planul Open, deoarece folosește sesiuni. Sarcinile de lucru pe planul Open pot rula doar în modul job sau în modul batch.
- 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}")
Pași pentru migrarea la Sampler V2
- Înlocuiește
from qiskit_ibm_runtime import Samplercufrom qiskit_ibm_runtime import SamplerV2 as Sampler. - Elimină orice instrucțiune
from qiskit_ibm_runtime import Options, deoarece clasaOptionsnu este utilizată de primitivele V2. În schimb, poți transmite opțiunile sub formă de dicționar la inițializarea claseiSamplerV2(de exemplu,sampler = Sampler(backend, options={"default_shots": 1024})), sau le poți seta după inițializare:sampler = Sampler(backend)
sampler.options.default_shots = 1024 - Consultă toate opțiunile acceptate și efectuează actualizările necesare.
- Grupează fiecare Circuit pe care vrei să-l rulezi împreună cu observabilele și valorile parametrilor pe care dorești să le aplici circuitului, într-un tuplu (un PUB). De exemplu, folosește
(circuit1, parameter_set1)dacă vrei să rulezicircuit1cuparameter_set1. Poți specifica opțional numărul de shot-uri dorite pentru acel PUB specific. - Actualizează metoda
run()a Sampler pentru a transmite lista de PUB-uri. De exemplu,run([(circuit1, parameter_set1)]). Poți specifica opționalshotsaici, ceea ce s-ar aplica tuturor PUB-urilor. - Rezultatele joburilor din Sampler V2 sunt grupate pe PUB-uri. Poți vedea datele de ieșire pentru fiecare PUB indexând până la acesta. În timp ce Sampler V2 returnează eșantioane neponderate, clasa de rezultate are o metodă de conveniență pentru a obține counts în schimb. De exemplu:
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()}")
Ai nevoie de numele registrului clasic pentru a obține rezultatele. În mod implicit, acesta se numește meas când folosești measure_all(). Când îți definești Circuit-ul, dacă creezi unul sau mai multe registre clasice cu un nume diferit de cel implicit, folosește acel nume pentru a obține rezultatele. Poți găsi numele registrului clasic rulând <circuit_name>.cregs. De exemplu, qc.cregs.
Exemple complete cu Sampler
Rularea unui singur experiment
Folosește Sampler pentru a determina counts sau distribuția de quasi-probabilitate a unui singur 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}")
Rulează mai multe experimente într-un singur job
Folosește Sampler pentru a determina numărul de apariții sau distribuțiile de cvasi-probabilitate ale mai multor Circuit-uri într-un singur job.
- 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}")
Rulează circuite parametrizate
Rulează mai multe experimente într-un singur job, folosind valori de parametri pentru a crește reutilizabilitatea circuitului.
- 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}")
Folosește sesiuni și opțiuni avansate
Explorează sesiunile și opțiunile avansate pentru a optimiza performanța Circuit-urilor pe QPU-uri.
Blocul de cod următor va returna o eroare pentru utilizatorii din planul Open, deoarece folosește sessions. Lucrările din planul Open pot rula doar în modul job sau în modul batch.
- 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}")
Next steps
- Află mai multe despre setarea opțiunilor în ghidul Specifică opțiuni.
- Află mai multe detalii despre Intrările și ieșirile Primitive.
- Experimentează cu tutorialul Inegalitatea CHSH.