Vai al contenuto principale

Migrare alle primitive V2 di Qiskit Runtime

avviso

Le primitive originali (denominate primitive V1), V1 Sampler e V1 Estimator, sono state deprecate in qiskit-ibm-runtime 0.23. Il loro supporto è stato rimosso il 15 agosto 2024.

Con la deprecazione delle primitive V1, tutto il codice dovrebbe essere migrato per utilizzare le interfacce V2. Questa guida descrive cosa è cambiato nelle primitive V2 di Qiskit Runtime (disponibili con qiskit-ibm-runtime 0.21.0) e perché, descrive in dettaglio ogni nuova primitiva e fornisce esempi per aiutarti a migrare il codice dall'utilizzo delle primitive legacy alle primitive V2. Gli esempi nella guida utilizzano tutti le primitive di Qiskit Runtime, ma in generale le stesse modifiche si applicano alle altre implementazioni di primitive. Le funzioni esclusive di Qiskit Runtime, come la mitigazione degli errori, rimangono esclusive di Qiskit Runtime.

Per informazioni sulle modifiche alle primitive di riferimento di Qiskit (ora denominate primitive statevector), consulta la sezione qiskit.primitives nella pagina delle modifiche delle funzionalità di Qiskit 1.0. Consulta StatevectorSampler e StatevectorEstimator per le implementazioni di riferimento delle primitive V2.

Panoramica

La versione 2 delle primitive è introdotta con una nuova classe base per Sampler ed Estimator (BaseSamplerV2 e BaseEstimatorV2), insieme a nuovi tipi per i loro input e output.

La nuova interfaccia ti permette di specificare un singolo circuito e più osservabili (se si utilizza Estimator) e insiemi di valori di parametri per quel circuito, in modo che le scansioni sugli insiemi di valori di parametri e sugli osservabili possano essere specificate in modo efficiente. In precedenza, dovevi specificare lo stesso circuito più volte per corrispondere alla dimensione dei dati da combinare. Inoltre, mentre puoi ancora utilizzare resilience_level (se si utilizza Estimator) come controllo semplice, le primitive V2 ti offrono la flessibilità di attivare o disattivare singoli metodi di mitigazione/soppressione degli errori per personalizzarli in base alle tue esigenze.

Per ridurre il tempo totale di esecuzione dei job, le primitive V2 accettano solo circuito e osservabili che utilizzano istruzioni supportate dalla QPU (quantum processing unit) di destinazione. Tali circuito e osservabili sono denominati circuito e osservabili ISA (instruction set architecture). Le primitive V2 non eseguono operazioni di layout, routing e traduzione. Consulta la documentazione sulla transpilazione per istruzioni su come trasformare i circuiti.

Sampler V2 è semplificato per concentrarsi sul suo compito principale di campionare il registro di output dall'esecuzione di circuiti quantistici. Restituisce i campioni, il cui tipo è definito dal programma, senza pesi. I dati di output sono anche separati in base ai nomi dei registri di output definiti dal programma. Questa modifica consente il supporto futuro per i circuiti con flusso di controllo classico.

Consulta il riferimento API di EstimatorV2 e il riferimento API di SamplerV2 per i dettagli completi.

Modifiche principali

Import

Per la compatibilità con le versioni precedenti, devi importare esplicitamente le primitive V2. Specificare import <primitiva>V2 as <primitiva> non è obbligatorio, ma facilita la transizione del codice a V2.

avviso

Dopo che le primitive V1 non saranno più supportate, import <primitiva> importerà la versione V2 della primitiva specificata.

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

Input and output

Input

Both SamplerV2 and EstimatorV2 take one or more primitive unified blocs (PUBs) as the input. Each PUB is a tuple that contains one circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.

  • Sampler V2 PUB format: (<circuit>, <parameter values>, <shots>), where <parameter values>and <shots> are optional.
  • Estimator V2 PUB format: (<circuit>, <observables>, <parameter values>, <precision>), where <parameter values>and <precision> are optional. Numpy broadcasting rules are used when combining observables and parameter values.

Additionally, the following changes have been made:

  • Estimator V2 has gained a precision argument in the run() method that specifies the targeted precision of the expectation value estimates.
  • Sampler V2 has the shots argument in its run() method.
Examples

Estimator V2 example that uses precision in run():

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

Sampler V2 example that uses shots in 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),
])

Output

The output is now in the PubResult format. A PubResult is the data and metadata resulting from a single PUB’s execution.

  • Estimator V2 continues to return expectation values.

  • The data portion of a Estimator V2 PubResult contains both expectation values and standard errors (stds). V1 returned variance in metadata.

  • Sampler V2 returns per-shot measurements in the form of bitstrings, instead of the quasi-probability distributions from the V1 interface. The bitstrings show the measurement outcomes, preserving the shot order in which they were measured.

  • Sampler V2 has convenience methods like get_counts() to help with migration.

  • The Sampler V2 result objects organize data in terms of their input circuits' classical register names, for compatibility with dynamic circuits. By default, the classical register name is meas, as shown in the following example. When defining your circuit, if you create one or more classical registers with a non-default name, use that name to get the results. You can find the classical register name by running <circuit_name>.cregs. For example, 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

Esempi di Estimator (input e output)

# 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

Sampler examples (input and output)

  # 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()

Esempio che utilizza registri di output diversi

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()}")

Opzioni

Le opzioni vengono specificate in modo diverso nelle primitive V2 nei seguenti modi:

  • SamplerV2 e EstimatorV2 ora hanno classi di opzioni separate. Puoi vedere le opzioni disponibili e aggiornare i valori delle opzioni durante o dopo l'inizializzazione della primitiva.
  • Invece del metodo set_options(), le opzioni delle primitive V2 dispongono del metodo update() che applica le modifiche all'attributo options.
  • Se non specifichi un valore per un'opzione, le viene assegnato un valore speciale Unset e vengono utilizzati i valori predefiniti del server.
  • Per le primitive V2, l'attributo options è del tipo Python dataclass. Puoi utilizzare il metodo integrato asdict per convertirlo in un dizionario.

Consulta il riferimento API per l'elenco delle opzioni disponibili.

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

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

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

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

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

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

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

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

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

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

Error mitigation and suppression

  • Because Sampler V2 returns samples without postprocessing, it does not support resilience levels.

  • Sampler V2 does not support optimization_level.

  • Estimator V2 will drop support for optimization_level on or around 30 September 2024.

  • Estimator V2 does not support resilience level 3. This is because resilience level 3 in V1 Estimator uses Probabilistic Error Cancellation (PEC), which is proven to give unbiased results at the cost of exponential processing time. Level 3 was removed to draw attention to that tradeoff. You can, however, still use PEC as the error mitigation method by specifying the pec_mitigation option.

  • Estimator V2 supports resilience_level 0-2, as described in the following table. These options are more advanced than their V1 counterparts. You can also explicitly turn on / off individual error mitigation / suppression methods.

    Level 1Level 2
    Measurement twirlingMeasurement twirling
    Readout error mitigationReadout error mitigation
    ZNE
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

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

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

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

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

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

Transpilation

V2 primitives support only circuits that adhere to the Instruction Set Architecture (ISA) of a particular backend. Because the primitives do not perform layout, routing, and translation operations, the corresponding transpilation options from V1 are not supported.

Job status

The V2 primitives have a new RuntimeJobV2 class, which inherits from BasePrimitiveJob. The status() method of this new class returns a string instead of a JobStatus enum from Qiskit. See the RuntimeJobV2 API reference for details.

job = estimator.run(...)

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

Steps to migrate to Estimator V2

  1. Replace from qiskit_ibm_runtime import Estimator with from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. Remove any from qiskit_ibm_runtime import Options statements, since the Options class is not used by V2 primitives. You can instead pass options as a dictionary when initializing the EstimatorV2 class (for example estimator = Estimator(backend, options={“dynamical_decoupling”: {“enable”: True}})), or set them after initialization:

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. Review all the supported options and make updates accordingly.

  4. Group each circuit you want to run with the observables and parameter values you want to apply to the circuit in a tuple (a PUB). For example, use (circuit1, observable1, parameter_set1) if you want to run circuit1 with observable1 and parameter_set1.

  5. You might need to reshape your arrays of observables or parameter sets if you want to apply their outer product. For example, an array of observables of shape (4, 1) and an array of parameter sets of shape (1, 6) will give you a result of (4, 6) expectation values. See the Numpy broadcasting rules for more details.

  6. You can optionally specify the precision you want for that specific PUB.

  7. Update the estimator run() method to pass in the list of PUBs. For example, run([(circuit1, observable1, parameter_set1)]). You can optionally specify a precision here, which would apply to all PUBs.

  8. Estimator V2 job results are grouped by PUBs. You can see the expectation value and standard error for each PUB by indexing to it. For example:

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

Estimator full examples

Run a single experiment

Use Estimator to determine the expectation value of a single circuit-observable pair.

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}")

Run multiple experiments in a single job

Use Estimator to determine the expectation values of multiple circuit-observable pairs.

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}")

Run parameterized circuits

Use Estimator to run multiple experiments in a single job, leveraging parameter values to increase circuit reusability. In the following example, notice that steps 1 and 2 are the same for V1 and V2.

import numpy as np

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

# Step 1: Map classical inputs to a quantum problem

theta = Parameter("θ")

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

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

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

# Step 2: Optimize problem for quantum execution.

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

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

from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Step 3: Execute using Qiskit primitives.

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

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

Use sessions and advanced options

Explore sessions and advanced options to optimize circuit performance on QPUs.

attenzione

The following code block will return an error for users on the Open Plan because it uses sessions. Workloads on the Open Plan can run only in job mode or batch mode.

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}")

Steps to migrate to Sampler V2

  1. Replace from qiskit_ibm_runtime import Sampler with from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. Remove any from qiskit_ibm_runtime import Options statements, since the Options class is not used by V2 primitives. You can instead pass options as a dictionary when initializing the SamplerV2 class (for example sampler = Sampler(backend, options={“default_shots”: 1024})), or set them after initialization:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. Review all the supported options and make updates accordingly.
  4. Group each circuit you want to run with the observables and parameter values you want to apply to the circuit in a tuple (a PUB). For example, use (circuit1, parameter_set1) if you want to run circuit1 with parameter_set1. You can optionally specify the shots you want for that specific PUB.
  5. Update the sampler run() method to pass in the list of PUBs. For example, run([(circuit1, parameter_set1)]). You can optionally specify shots here, which would apply to all PUBs.
  6. Sampler V2 job results are grouped by PUBs. You can see the output data for each PUB by indexing to it. While Sampler V2 returns unweighted samples, the result class has a convenience method to get counts instead. For example:
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()}")
nota

You need the classical register name to get the results. By default, it is named meas when you use measure_all(). When defining your circuit, if you create one or more classical registers with a non-default name, use that name to get the results. You can find the classical register name by running <circuit_name>.cregs. For example, qc.cregs.

Sampler full examples

Run a single experiment

Use Sampler to determine the counts or quasi-probability distribution of a single circuit.

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

service = QiskitRuntimeService()

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

n_qubits = 127

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

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

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

Run multiple experiments in a single job

Use Sampler to determine the counts or quasi-probability distributions of multiple circuits in one job.

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()}")

Run parameterized circuits

Run several experiments in a single job, leveraging parameter values to increase circuit reusability.

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()}")

Use sessions and advanced options

Explore sessions and advanced options to optimize circuit performance on QPUs.

attenzione

The following code block will return an error for users on the Open Plan because it uses sessions. Workloads on the Open Plan can run only in job mode or batch mode.

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()}")

Passi successivi

Raccomandazioni