Saltar al contenido principal

Especificar opciones

Versiones de paquetes

El código de esta página fue desarrollado con los siguientes requisitos. Recomendamos usar estas versiones o más recientes.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Puedes usar opciones para personalizar las primitivas Estimator y Sampler. Esta sección se centra en cómo especificar opciones de las primitivas de Qiskit Runtime. Aunque la interfaz del método run() de las primitivas es común a todas las implementaciones, sus opciones no lo son. Consulta las referencias de API correspondientes para obtener información sobre las opciones de qiskit.primitives y qiskit_aer.primitives.

Notas sobre cómo especificar opciones en las primitivas:

  • SamplerV2 y EstimatorV2 tienen clases de opciones separadas. Puedes ver las opciones disponibles y actualizar los valores de las opciones durante o después de la inicialización de la primitiva.
  • Usa el método update() para aplicar cambios al atributo options.
  • Si no especificas un valor para una opción, se le asigna el valor especial Unset y se usan los valores predeterminados del servidor.
  • El atributo options es del tipo Python dataclass. Puedes usar el método incorporado asdict para convertirlo en un diccionario.

Configurar opciones de las primitivas

Puedes configurar opciones al inicializar la primitiva, después de inicializarla, o en el método run(). Consulta la sección de reglas de precedencia para entender qué sucede cuando la misma opción se especifica en varios lugares.

Inicialización de la primitiva

Puedes pasar una instancia de la clase de opciones o un diccionario al inicializar una primitiva, que luego hace una copia de esas opciones. Por lo tanto, modificar el diccionario original o la instancia de opciones no afecta a las opciones que posee la primitiva.

Clase de opciones

Al crear una instancia de la clase EstimatorV2 o SamplerV2, puedes pasar una instancia de la clase de opciones. Esas opciones se aplicarán cuando uses run() para realizar el cálculo. Especifica las opciones con este formato: options.option.sub-option.sub-sub-option = choice. Por ejemplo: options.dynamical_decoupling.enable = True

Ejemplo:

SamplerV2 y EstimatorV2 tienen clases de opciones separadas (EstimatorOptions y SamplerOptions).

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime.options import EstimatorOptions

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

options = EstimatorOptions(
resilience_level=2,
resilience={"zne_mitigation": True, "zne": {"noise_factors": [1, 3, 5]}},
)

# or...
options = EstimatorOptions()
options.resilience_level = 2
options.resilience.zne_mitigation = True
options.resilience.zne.noise_factors = [1, 3, 5]

estimator = Estimator(mode=backend, options=options)

Diccionario

Puedes especificar opciones como un diccionario al inicializar la primitiva.

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,
"resilience": {
"zne_mitigation": True,
"zne": {"noise_factors": [1, 3, 5]},
},
},
)

Actualizar opciones tras la inicialización

Puedes especificar las opciones con este formato: primitive.options.option.sub-option.sub-sub-option = choice para aprovechar el autocompletado, o usar el método update() para hacer actualizaciones masivas.

Las clases de opciones de SamplerV2 y EstimatorV2 (EstimatorOptions y SamplerOptions) no necesitan ser instanciadas si estás configurando opciones después de inicializar la primitiva.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

estimator = Estimator(mode=backend)

# 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={"zne_mitigation": True}
)

Método Run()

Los únicos valores que puedes pasar a run() son los definidos en la interfaz. Es decir, shots para Sampler y precision para Estimator. Esto sobreescribe cualquier valor establecido para default_shots o default_precision para la ejecución actual.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.circuit.library import random_iqp
from qiskit.transpiler import generate_preset_pass_manager

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

circuit1 = random_iqp(3)
circuit1.measure_all()
circuit2 = random_iqp(3)
circuit2.measure_all()

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend
)

transpiled1 = pass_manager.run(circuit1)
transpiled2 = pass_manager.run(circuit2)

sampler = Sampler(mode=backend)
# Default shots to use if not specified in run()
sampler.options.default_shots = 500
# Sample two circuits at 128 shots each.
sampler.run([transpiled1, transpiled2], shots=128)

# Sample two circuits with different numbers of shots.
# 100 shots is used for transpiled1 and 200 for transpiled.
sampler.run([(transpiled1, None, 100), (transpiled2, None, 200)])
<RuntimeJobV2('d5k96cn853es738djikg', 'sampler')>

Casos especiales

Nivel de resiliencia (solo Estimator)

El nivel de resiliencia no es en realidad una opción que impacte directamente la consulta a la primitiva, sino que especifica un conjunto base de opciones seleccionadas sobre las cuales construir. En general, el nivel 0 desactiva toda la mitigación de errores, el nivel 1 activa las opciones para la mitigación de errores de medición, y el nivel 2 activa las opciones para la mitigación de errores de compuertas y de medición.

Cualquier opción que especifiques manualmente además del nivel de resiliencia se aplica encima del conjunto base de opciones definido por dicho nivel. Por lo tanto, en principio podrías establecer el nivel de resiliencia en 1 y luego desactivar la mitigación de medición, aunque esto no es recomendable.

En el siguiente ejemplo, establecer el nivel de resiliencia en 0 desactiva inicialmente zne_mitigation, pero estimator.options.resilience.zne_mitigation = True sobreescribe la configuración relevante de estimator.options.resilience_level = 0.

from qiskit_ibm_runtime import EstimatorV2, QiskitRuntimeService

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

estimator = EstimatorV2(backend)

estimator.options.default_shots = 100
estimator.options.resilience_level = 0
estimator.options.resilience.zne_mitigation = True

Shots (solo Sampler)

El método SamplerV2.run acepta dos argumentos: una lista de PUBs, cada uno de los cuales puede especificar un valor de shots específico para ese PUB, y un argumento de palabra clave shots. Estos valores de shots forman parte de la interfaz de ejecución del Sampler y son independientes de las opciones del Sampler de Runtime. Tienen precedencia sobre cualquier valor especificado como opción, con el fin de cumplir con la abstracción del Sampler.

Sin embargo, si shots no está especificado por ningún PUB ni en el argumento de palabra clave de run (o si todos son None), se usa el valor de shots de las opciones, principalmente default_shots.

En resumen, este es el orden de precedencia para especificar shots en el Sampler, para cualquier PUB en particular:

  1. Si el PUB especifica shots, se usa ese valor.
  2. Si el argumento de palabra clave shots está especificado en run, se usa ese valor.
  3. Si num_randomizations y shots_per_randomization están especificados como opciones de twirling, los shots son el producto de esos valores.
  4. Si sampler.options.default_shots está especificado, se usa ese valor.

Por lo tanto, si los shots están especificados en todos los lugares posibles, se usa el de mayor precedencia (shots especificados en el PUB).

Precisión (solo Estimator)

La precisión es análoga a los shots, descrita en la sección anterior, excepto que las opciones del Estimator contienen tanto default_shots como default_precision. Además, dado que el gate-twirling está habilitado por defecto, el producto de num_randomizations y shots_per_randomization tiene precedencia sobre esas dos opciones.

Específicamente, para cualquier PUB del Estimator en particular:

  1. Si el PUB especifica precisión, se usa ese valor.
  2. Si el argumento de palabra clave precision está especificado en run, se usa ese valor.
  3. Si num_randomizations y shots_per_randomization están especificados como opciones de twirling (habilitadas por defecto), se usa su producto para controlar la cantidad de datos.
  4. Si estimator.options.default_shots está especificado, se usa ese valor para controlar la cantidad de datos.
  5. Si estimator.options.default_precision está especificado, se usa ese valor.

Por ejemplo, si la precisión está especificada en los cuatro lugares, se usa el de mayor precedencia (precisión especificada en el PUB).

nota

La precisión escala inversamente con el uso. Es decir, cuanto menor es la precisión, más tiempo de QPU se necesita para ejecutar.

Opciones de uso frecuente

Hay muchas opciones disponibles, pero las siguientes son las más utilizadas:

Shots

Para algunos algoritmos, establecer un número específico de shots es una parte fundamental de sus rutinas. Los shots (o la precisión) pueden especificarse en varios lugares. Su orden de prioridad es el siguiente:

Para cualquier PUB del Sampler:

  1. Shots de valor entero contenidos en el PUB
  2. El valor de run(...,shots=val)
  3. El valor de options.default_shots

Para cualquier PUB del Estimator:

  1. Precisión de valor flotante contenida en el PUB
  2. El valor de run(...,precision=val)
  3. El valor de options.default_shots
  4. El valor de options.default_precision

Ejemplo:

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.circuit.library import random_iqp
from qiskit.transpiler import generate_preset_pass_manager

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

circuit1 = random_iqp(3)
circuit1.measure_all()
circuit2 = random_iqp(3)
circuit2.measure_all()

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend
)

transpiled1 = pass_manager.run(circuit1)
transpiled2 = pass_manager.run(circuit2)

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

# Setting options after primitive initialization
# This uses auto-complete.
sampler.options.default_shots = 2000

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

# Sample two circuits at 128 shots each.
sampler.run([transpiled1, transpiled2], shots=128)
<RuntimeJobV2('d5k96icjt3vs73ds5t0g', 'sampler')>

Tiempo máximo de ejecución

El tiempo máximo de ejecución (max_execution_time) limita el tiempo que puede ejecutarse un trabajo. Si un trabajo supera este límite de tiempo, se cancela de forma forzada. Este valor se aplica a trabajos individuales, ya sea que se ejecuten en modo de trabajo, sesión o lote.

El valor se establece en segundos, basado en el tiempo cuántico (no en el tiempo de reloj), que es la cantidad de tiempo que el QPU está dedicado a procesar tu trabajo. Se ignora cuando se usa el modo de prueba local, porque ese modo no usa tiempo cuántico.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

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

estimator = Estimator(mode=backend)

estimator.options.max_execution_time = 2500

Desactivar toda la mitigación y supresión de errores

Puedes desactivar toda la mitigación y supresión de errores si, por ejemplo, estás investigando tus propias técnicas de mitigación. Para lograrlo, en el caso de EstimatorV2, establece resilience_level = 0. Para SamplerV2 no es necesario ningún cambio, ya que ninguna opción de mitigación o supresión de errores está habilitada por defecto.

Ejemplo:

Desactiva toda la mitigación y supresión de errores en Estimator.

from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

# Define the service. This allows you to access IBM QPU.
service = QiskitRuntimeService()

# Get a backend
backend = service.least_busy(operational=True, simulator=False)

# Define Estimator
estimator = Estimator(backend)

options = estimator.options

# Turn off all error mitigation and suppression
options.resilience_level = 0

Próximos pasos

Recomendaciones