Skip to main content
IBM Quantum Platform

Migrar a las primitivas Qiskit Runtime V2

Warning

Las primitivas originales (conocidas como las primitivas « V1 »), V1 Sampler y V1 Estimator, han quedado obsoletas en qiskit-ibm-runtime0.23. Su apoyo se retiró el 15 de agosto de 2024.

Con la desaparición de las primitivas V1, todo el código debe migrar para utilizar las interfaces V2. Esta guía describe qué ha cambiado en las primitivas Qiskit Runtime V2 (disponibles con qiskit-ibm-runtime 0.21.0 ) y por qué, describe cada nueva primitiva en detalle y ofrece ejemplos para ayudarte a migrar código desde el uso de las primitivas heredadas a las primitivas V2. Todos los ejemplos de la guía utilizan las primitivas Qiskit Runtime, pero en general, los mismos cambios se aplican a las demás implementaciones de primitivas. Las funciones exclusivas de Qiskit Runtime, como la mitigación de errores, siguen siendo exclusivas de Qiskit Runtime.

Para obtener información sobre los cambios en las primitivas de referencia de Qiskit (ahora llamadas primitivas de vector de estado ), consulte la sección qiskit.primitives en la página de cambios de características de Qiskit 1.0. Véase StatevectorSampler y StatevectorEstimator para las implementaciones de referencia primitivas de V2.


Visión general

Se introduce la versión 2 de las primitivas con una nueva clase base tanto para el Muestreador como para el Estimador ( BaseSamplerV2 y BaseEstimatorV2 ), junto con nuevos tipos para sus entradas y salidas.

La nueva interfaz permite especificar un único circuito y múltiples observables (si se utiliza Estimator) y conjuntos de valores de parámetros para ese circuito, de modo que los barridos sobre conjuntos de valores de parámetros y observables puedan especificarse de manera eficiente. Antes había que especificar varias veces el mismo circuito para que coincidiera con el tamaño de los datos que se iban a combinar. Además, aunque puede seguir utilizando resilience_level (si utiliza Estimator) como mando simple, las primitivas de V2 le ofrecen la flexibilidad de activar o desactivar métodos individuales de mitigación/supresión de errores para personalizarlos según sus necesidades.

Para reducir el tiempo total de ejecución del trabajo, las primitivas V2 sólo aceptan circuitos y observables que utilicen instrucciones soportadas por la QPU (unidad de procesamiento cuántico) de destino. Dichos circuitos y observables se denominan circuitos y observables de arquitectura de conjunto de instrucciones (ISA). V2 no realizan operaciones de diseño, enrutamiento y traducción. Consulte la documentación de transpilación para obtener instrucciones para transformar circuitos.

El muestreador V2 se simplifica para centrarse en su tarea principal de muestrear el registro de salida de la ejecución de circuitos cuánticos. Devuelve las muestras, cuyo tipo define el programa, sin pesos. Los datos de salida también están separados por los nombres de los registros de salida definidos por el programa. Este cambio permite la futura compatibilidad con circuitos con flujo de control clásico.

Consulte la referencia de la API EstimatorV2 y la referencia de la API SamplerV2 para obtener detalles completos.


Cambios importantes

Importar

Por compatibilidad con versiones anteriores, debe importar explícitamente las primitivas V2. Especificar import <primitive>V2 as <primitive> no es necesario, pero facilita la transición del código a V2.

Warning

Cuando las primitivas V1 dejen de ser compatibles, import <primitive> importará la versión V2 de la primitiva especificada.

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

Entrada y salida

Entrada

Tanto SamplerV2 como EstimatorV2 toman como entrada uno o varios bloques unificados primitivos (PUB). Cada PUB es una tupla que contiene un circuito y los datos emitidos a ese circuito, que pueden ser múltiples observables y parámetros. Cada PUB devuelve un resultado.

  • Muestreador V2 PUB formato: (<circuit>, <parameter values>, <shots>), donde <parameter values>y <shots> son opcionales.
  • 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.

Además, se han introducido los siguientes cambios:

  • El estimador V2 ha ganado un argumento precision en el método run() que especifica la precisión objetivo de las estimaciones del valor de la expectativa.
  • El muestreador V2 tiene el argumento shots en su método run() .
Ejemplos

Estimador V2 ejemplo que utiliza la precisión en 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 ejemplo que utiliza tomas en 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),
])

Resultado

La salida tiene ahora el formato PubResult formato. Un PubResult son los datos y metadatos resultantes de la ejecución de un único PUB.

  • El estimador V2 sigue devolviendo valores de expectativa.

  • La parte data de un estimador V2 PubResult contiene tanto los valores de las expectativas como los errores estándar (stds). V1 devuelve la varianza en los metadatos.

  • El muestreador «V2 » devuelve mediciones por disparo en forma de cadenas de bits , en lugar de las distribuciones de cuasiprobabilidad de la interfaz «V1 ». Las cadenas de bits muestran los resultados de las mediciones, conservando el orden en que se realizaron.

  • Sampler V2 dispone de métodos prácticos como get_counts() para facilitar la migración.

  • 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

Ejemplos de estimadores (entrada y salida)

# 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

Ejemplos de muestreo (entrada y salida)

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

Ejemplo que utiliza diferentes registros de salida

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: "
f"{pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: "
f"{pub_result.data.beta.get_counts()}")

Opciones

Las opciones se especifican de forma diferente en las primitivas V2 de estas maneras:

  • SamplerV2 y EstimatorV2 tienen ahora clases de opciones separadas. Puede ver las opciones disponibles y actualizar los valores de las opciones durante o después de la inicialización de la primitiva.
  • En lugar del método set_options() , las opciones primitivas V2 tienen el método update() que aplica los cambios al atributo options .
  • Si no se especifica un valor para una opción, se le da un valor especial de Unset y se utilizan los valores predeterminados del servidor.
  • Para las primitivas V2, el atributo options es del tipo dataclass Python. Puede utilizar el método incorporado asdict para convertirlo en un diccionario.

Consulte la referencia de la API para ver la lista de opciones disponibles.

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

Mitigación y supresión de errores

  • Dado que Sampler V2 devuelve muestras sin postprocesado, no admite niveles de resiliencia.

  • El muestreador V2 no admite optimization_level.

  • Estimator V2 dejará de soportar optimization_level alrededor del 30 de septiembre de 2024.

  • Estimator V2 no admite el nivel de resistencia 3. Esto se debe a que el nivel 3 de resiliencia en V1 Estimator utiliza la Cancelación Probabilística de Errores (PEC), que ha demostrado dar resultados insesgados a costa de un tiempo de procesamiento exponencial. El nivel 3 se suprimió para llamar la atención sobre esa compensación. No obstante, puede seguir utilizando PEC como método de mitigación de errores especificando la opción pec_mitigation .

  • Estimator V2 admite resilience_level 0-2, como se describe en la tabla siguiente. Estas opciones son más avanzadas que sus homólogas de V1. También puede activar/desactivar explícitamente métodos individuales de mitigación/supresión de errores.

    Nivel 1
    Nivel 2
    Giro de medidasGiro de medidas
    Mitigación de errores de lecturaMitigación de errores de lectura
    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: "
f"{sampler.options.dynamical_decoupling.sequence_type}")

Transpilación

V2 sólo admiten circuitos que se adhieran a la arquitectura de conjunto de instrucciones (ISA) de un backend concreto. Dado que las primitivas no realizan operaciones de diseño, enrutamiento y traducción, las opciones de transpilación correspondientes de V1 no son compatibles.

Estado del trabajo

Las primitivas V2 tienen una nueva clase RuntimeJobV2 , que hereda de BasePrimitiveJob. El método status() de esta nueva clase devuelve una cadena en lugar de un enum JobStatus de Qiskit. Para más detalles, consulte la referencia de la API RuntimeJobV2.

job = estimator.run(...)

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

Pasos para migrar a Estimator V2

  1. Sustituya from qiskit_ibm_runtime import Estimator por from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. Elimine cualquier declaración from qiskit_ibm_runtime import Options , ya que la clase Options no es utilizada por las primitivas V2. En su lugar, puede pasar las opciones como un diccionario al inicializar la clase EstimatorV2 (por ejemplo estimator = Estimator(backend, options={“dynamical_decoupling”: {“enable”: True}})), o establecerlas después de la inicialización:

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. Revise todas las opciones compatibles y actualícelas en consecuencia.

  4. Agrupa cada circuito que quieras ejecutar con los observables y valores de parámetros que quieras aplicar al circuito en una tupla (un PUB ). Por ejemplo, utilice (circuit1, observable1, parameter_set1) si desea ejecutar circuit1 con observable1 y parameter_set1.

  5. Es posible que tenga que remodelar sus matrices de observables o conjuntos de parámetros si desea aplicar su producto exterior. Por ejemplo, una matriz de observables de forma (4, 1) y una matriz de conjuntos de parámetros de forma (1, 6) le dará un resultado de (4, 6) valores de expectativa. Consulte las reglas de difusión de Numpy para obtener más detalles.

  6. Opcionalmente, puede especificar la precisión que desea para ese PUB específico.

  7. Actualiza el método de run() Estimator para que admita la lista de PUB. Por ejemplo, run([(circuit1, observable1, parameter_set1)]). Si lo deseas, puedes especificar un valor precision aquí, que se aplicaría a todos los PUB.

  8. Estimador V2 los resultados de los trabajos se agrupan por PUBs. Puede ver el valor esperado y el error estándar de cada PUB indexándolo. Por ejemplo:

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

Ejemplos completos del estimador

Realizar un único experimento

Utilice el Estimador para determinar el valor esperado de un único par circuito-observable.

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

Ejecutar múltiples experimentos en un solo trabajo

Utilice el Estimador para determinar los valores esperados de múltiples pares circuito-observable.

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

Ejecutar circuitos parametrizados

Utilice Estimator para ejecutar varios experimentos en un solo trabajo, aprovechando los valores de los parámetros para aumentar la reutilización de los circuitos. En el siguiente ejemplo, observe que los pasos 1 y 2 son los mismos para V1 y 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}")

Utilizar sesiones y opciones avanzadas

Explora las sesiones y las opciones avanzadas para optimizar el rendimiento de los circuitos en las QPU.

Caution

El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en modo trabajo o por lotes.

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: "
          f"{another_result[0].data.evs}")
    print(f" > More Metadata: {another_result[0].metadata}")

Pasos para migrar a Sampler V2

  1. Sustituya from qiskit_ibm_runtime import Sampler por from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. Elimine cualquier declaración from qiskit_ibm_runtime import Options , ya que la clase Options no es utilizada por las primitivas V2. En su lugar, puede pasar las opciones como un diccionario al inicializar la clase SamplerV2 (por ejemplo sampler = Sampler(backend, options={“default_shots”: 1024})), o establecerlas después de la inicialización:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. Revise todas las opciones compatibles y actualícelas en consecuencia.
  4. Agrupa en una tupla ( PUB ) cada circuito que quieras ejecutar junto con los observables y los valores de los parámetros que quieras aplicar al circuito. Por ejemplo, utiliza (circuit1, parameter_set1) si quieres ejecutar circuit1 con parameter_set1. Si lo deseas, puedes indicar las tomas que quieres para ese « PUB » concreto.
  5. Actualiza el método de run() Sampler para que admita la lista de PUB. Por ejemplo, run([(circuit1, parameter_set1)]). Si lo deseas, puedes indicarlo shots aquí; esto se aplicaría a todos los PUB.
  6. Muestreador V2 los resultados de los trabajos se agrupan por PUBs. Puede ver los datos de salida de cada PUB indexándolos. Mientras que Sampler V2 devuelve muestras no ponderadas, la clase resultante tiene un método conveniente para obtener recuentos en su lugar. Por ejemplo:
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()}")
Note

Necesita el nombre de registro clásico para obtener los resultados. Por defecto, se denomina meas cuando se utiliza measure_all(). Cuando definas tu circuito, si creas uno o más registros clásicos con un nombre no predeterminado, utiliza ese nombre para obtener los resultados. Puede encontrar el nombre de registro clásico ejecutando <circuit_name>.cregs. Por ejemplo, qc.cregs.


Ejemplos completos del muestreador

Realizar un único experimento

Utilice Sampler para determinar los recuentos o la distribución cuasi-probabilística de un circuito único.

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

Ejecutar múltiples experimentos en un solo trabajo

Utilice Sampler para determinar los recuentos o las distribuciones de cuasiprobabilidad de varios circuitos en un trabajo.

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

Ejecutar circuitos parametrizados

Ejecute varios experimentos en un solo trabajo, aprovechando los valores de los parámetros para aumentar la reutilización de los circuitos.

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: "
      f"{pub_result.data.meas.get_counts()}")

Utilizar sesiones y opciones avanzadas

Explora las sesiones y las opciones avanzadas para optimizar el rendimiento de los circuitos en las QPU.

Caution

El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en modo trabajo o por lotes.

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

Próximos pasos

Recomendaciones
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.