Nota
Esta página fue generada a partir de docs/tutorials/how-to-getting-started-with-estimator.ipynb.
Primeros pasos con la primitiva Estimator¶
En este tutorial, te mostraremos cómo configurar la primitiva Estimator
de Qiskit Runtime, explorar las diferentes opciones que puedes usar para configurarlo e invocar la primitiva de manera eficiente dentro de una sesión.
Primitivas¶
Primitives are core functions that make it easier to build modular algorithms and applications.
La versión inicial de Qiskit Runtime incluye dos primitivas:
Sampler: Generates quasi-probability distribution from input circuits.
Estimator: Calcula los valores esperados de los circuitos de entrada y observables.
En este tutorial nos centraremos en la primitiva Estimator
. Hay un tutorial separado sobre los Primeros pasos con la primitiva Sampler.
Utilizar la primitiva Estimator¶
Similar a la clase base Backend
, hay una clase base Estimator
definida en Qiskit Terra que estandariza la forma en que los usuarios interactúan con todas las implementaciones de Estimator
. Esto permite a los usuarios cambiar fácilmente su elección de simulador o dispositivo para realizar cálculos de valor esperado, incluso si la implementación subyacente es diferente.
En esta sección, utilizaremos la implementación predeterminada en Qiskit Terra, que utiliza un simulador de vector de estado local.
1. Crear un circuito¶
Para un cálculo básico del valor esperado, necesitarás al menos un circuito cuántico para preparar nuestro sistema en un estado cuántico preciso para el estudio. Todos nuestros ejemplos tienen circuitos, pero puedes usar Qiskit para crear el tuyo propio. Para aprender a crear circuitos con Qiskit, consulta el Tutorial de aspectos básicos de un circuito.
[1]:
from qiskit.circuit.random import random_circuit
circuit = random_circuit(2, 2, seed=0).decompose(reps=1)
display(circuit.draw("mpl"))

2. Crear un observable para medir¶
También necesitarás al menos un observable para medir. Los observables representan propiedades físicas de un sistema cuántico (como la energía o el espín) y permiten medir dichas propiedades (como sus valores esperados) para un estado dado de nuestro sistema. Para simplificar, puedes usar la clase SparsePauliOp en Qiskit para definirlos, como se ilustra en el siguiente ejemplo.
[2]:
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp("XZ")
print(f">>> Observable: {observable.paulis}")
>>> Observable: ['XZ']
3. Inicializar una clase Estimator¶
The next step is to create an instance of an Estimator
class, which can be any of the subclasses that comply with the base specification. For simplicity, we will use Qiskit Terra’s qiskit.primitives.Estimator
class, based on the Statevector construct (algebraic simulation).
[3]:
from qiskit.primitives import Estimator
estimator = Estimator()
4. Invocar el Estimator y obtener resultados¶
Para calcular los valores esperados, invoca al método run()
de la instancia Estimator
que acabas de crear y pasa el circuito y el observable como parámetros de entrada. Esta llamada de método es asíncrona y obtendrás un objeto Job
. Puedes usar este objeto para consultar información como job_id()
y status()
.
[4]:
job = estimator.run(circuit, observable)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: 85a54b84-7ce2-49f8-b614-cc39ef8eafcc
>>> Job Status: JobStatus.DONE
El método result()
del trabajo (job) devolverá el EstimatorResult
, que incluye tanto los valores esperados como los metadatos del trabajo.
[5]:
result = job.result()
print(f">>> {result}")
print(f" > Expectation value: {result.values[0]}")
>>> EstimatorResult(values=array([0.85347811]), metadata=[{}])
> Expectation value: 0.8534781134132173
Puedes seguir invocando al método run()
nuevamente con las diferentes entradas:
[6]:
circuit = random_circuit(2, 2, seed=1).decompose(reps=1)
observable = SparsePauliOp("IY")
job = estimator.run(circuit, observable)
result = job.result()
display(circuit.draw("mpl"))
print(f">>> Observable: {observable.paulis}")
print(f">>> Expectation value: {result.values[0]}")

>>> Observable: ['IY']
>>> Expectation value: -1.582539029327245e-16
También puedes proporcionar entradas compuestas al método run()
:
[7]:
circuits = (
random_circuit(2, 2, seed=0).decompose(reps=1),
random_circuit(2, 2, seed=1).decompose(reps=1),
)
observables = (
SparsePauliOp("XZ"),
SparsePauliOp("IY"),
)
job = estimator.run(circuits, observables)
result = job.result()
[display(cir.draw("mpl")) for cir in circuits]
print(f">>> Observables: {[obs.paulis for obs in observables]}")
print(f">>> Expectation values: {result.values.tolist()}")


>>> Observables: [PauliList(['XZ']), PauliList(['IY'])]
>>> Expectation values: [0.8534781134132173, -1.582539029327245e-16]
O usar circuitos parametrizados:
[8]:
from qiskit.circuit.library import RealAmplitudes
circuit = RealAmplitudes(num_qubits=2, reps=2).decompose(reps=1)
observable = SparsePauliOp("ZI")
parameter_values = [0, 1, 2, 3, 4, 5]
job = estimator.run(circuit, observable, parameter_values)
result = job.result()
display(circuit.draw("mpl"))
print(f">>> Observable: {observable.paulis}")
print(f">>> Parameter values: {parameter_values}")
print(f">>> Expectation value: {result.values[0]}")

>>> Observable: ['ZI']
>>> Parameter values: [0, 1, 2, 3, 4, 5]
>>> Expectation value: -0.6485568434766461
Usar Estimator de Qiskit Runtime¶
En esta sección, veremos cómo usar la implementación de Qiskit Runtime de la primitiva Estimator
.
1. Inicializar la cuenta¶
Dado que Estimator
de Qiskit Runtime es un servicio administrado, primero deberás inicializar tu cuenta. Luego puedes seleccionar el simulador o el backend real que deseas usar para calcular el valor esperado.
Sigue la guía de primeros pasos si aún no tienes una cuenta configurada.
[9]:
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService(channel="ibm_quantum")
backend = service.backend("ibmq_qasm_simulator")
2. Crear un circuito y un observable¶
Al igual que en la sección anterior, necesitarás al menos un circuito y un observable como entradas para la primitiva Estimator
.
[10]:
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp
circuit = random_circuit(2, 2, seed=0).decompose(reps=1)
display(circuit.draw("mpl"))
observable = SparsePauliOp("XZ")
print(f">>> Observable: {observable.paulis}")

>>> Observable: ['XZ']
3. Inicializar Estimator de Qiskit Runtime¶
Aquí estamos inicializando una instancia de qiskit_ibm_runtime.Estimator
en lugar de qiskit.primitives.Estimator
para usar la implementación de Estimator
de Qiskit Runtime.
When you initialize the Estimator
, you’ll need to pass in the backend you previously selected as the target device (or simulator), using the backend
parameter.
[11]:
from qiskit_ibm_runtime import Estimator
estimator = Estimator(backend=backend)
4. Invocar el Estimator y obtener resultados¶
Luego puedes invocar al método run()
para calcular los valores esperados para los circuitos de entrada y los observables.
[12]:
job = estimator.run(circuit, observable)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: cdkrlhian60ka16e78dg
>>> Job Status: JobStatus.RUNNING
[13]:
result = job.result()
print(f">>> {result}")
print(f" > Expectation value: {result.values[0]}")
print(f" > Metadata: {result.metadata[0]}")
>>> EstimatorResult(values=array([0.859]), metadata=[{'variance': 0.262119, 'shots': 4000}])
> Expectation value: 0.859
> Metadata: {'variance': 0.262119, 'shots': 4000}
Opciones¶
Las primitivas vienen con varias opciones que se agrupan en diferentes categorías. Las opciones de uso común, como resilience_level
, se encuentran en el primer nivel.
Puedes utilizar la clase Options para especificar distintas opciones.
En el siguiente ejemplo, creamos una instancia de la clase Options
. optimization_level
es una opción de primer nivel y se puede pasar como un parámetro de entrada. Las opciones relacionadas con el entorno de ejecución se pasan mediante el parámetro environment
.
[14]:
from qiskit_ibm_runtime import Options
options = Options(optimization_level=3, environment={"log_level": "INFO"})
Options
soporta el autocompletado. Una vez que creas una instancia de la clase Options
, puedes usar la función de autocompletar para ver qué opciones están disponibles. Si eliges una de las categorías, puedes usar la función de autocompletar nuevamente para ver qué opciones están disponibles en esa categoría.
[15]:
from qiskit_ibm_runtime import Options
options = Options()
options.resilience_level = 1
options.execution.shots = 2048
Al crear una instancia de la clase Estimator
, puedes pasar las options
que acabas de crear. Esas opciones se aplicarán cuando uses run()
para realizar el cálculo.
[16]:
estimator = Estimator(backend=backend, options=options)
result = estimator.run(circuit, observable).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'variance': 0.2847862243652344, 'shots': 2048, 'readout_mitigation_num_twirled_circuits': 16, 'readout_mitigation_shots_calibration': 8192}
También puedes pasar opciones al método run()
. Esto sobrescribirá las opciones que especificaste al crear la instancia de Estimator
para esa ejecución en particular.
Dado que la mayoría de los usuarios solo sobrescribirán un puñado de opciones a nivel del trabajo (job), no es necesario especificar la categoría en la que se encuentran las opciones. El siguiente código, por ejemplo, especifica shots=1024
en lugar de execution={"shots": 1024}
(que también es válido).
[17]:
estimator = Estimator(backend=backend, options=options)
result = estimator.run(circuit, observable, shots=1024).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'variance': 0.2814788818359375, 'shots': 1024, 'readout_mitigation_num_twirled_circuits': 16, 'readout_mitigation_shots_calibration': 8192}
Supresión y mitigación de errores¶
optimization_level
y resilience_level
se utilizan para configurar la supresión y mitigación de errores.
Estimator
admite optimization_level
0-3 y resilience_level
0-3.
Las opciones de mitigación avanzadas se pueden especificar en resilience
.
[18]:
from qiskit_ibm_runtime import Options
# optimization_level=3 adds dynamical decoupling
# resilience_level=1 adds readout error mitigation
options = Options(optimization_level=3, resilience_level=1)
[19]:
estimator = Estimator(backend=backend, options=options)
result = estimator.run(circuit, observable).result()
print(f">>> Expectation value: {result.values[0]}")
print(f">>> Metadata: {result.metadata[0]}")
>>> Expectation value: 0.8485
>>> Metadata: {'variance': 0.28004774999999993, 'shots': 4000, 'readout_mitigation_num_twirled_circuits': 16, 'readout_mitigation_shots_calibration': 8192}
Sesión¶
Una sesión de Qiskit Runtime te permite agrupar una colección de llamadas iterativas a la computadora cuántica. Una sesión se inicia cuando se inicia el primer trabajo (job) dentro de la sesión. Si la sesión está activa, el planificador prioriza los trabajos posteriores dentro de la sesión para minimizar el retraso artificial dentro de un algoritmo iterativo. Los datos utilizados dentro de una sesión, como los circuitos transpilados, también se almacenan en caché para evitar una sobrecarga innecesaria.
Duración de la sesión¶
Cuando se inicia una sesión, se le asigna un valor máximo de tiempo de espera de sesión. Puedes establecer este valor utilizando el parámetro max_time
.
Si no especificas un valor de tiempo de espera, se establece en el tiempo de ejecución máximo del trabajo inicial y es el menor de estos valores:
El límite del sistema (consulta ¿Cuál es el tiempo máximo de ejecución para un job de Qiskit Runtime?).
El
max_execution_time
definido por el programa.
Una vez alcanzado este límite de tiempo, la sesión se cierra de forma permanente.
Una sesión también tiene un valor de tiempo de espera interactivo. Si no hay trabajos (jobs) de sesión encolados dentro de esa ventana, la sesión se desactiva temporalmente y se reanuda la selección normal de trabajos. Este valor de tiempo de espera interactivo lo establece el sistema y no se puede sobrescribir.
Invocar Estimator.run dentro de una sesión¶
Puedes crear una sesión de Qiskit Runtime usando el administrador de contexto (with ...:
), que automáticamente abre y cierra la sesión por ti. Puedes invocar Estimator.run
una o más veces dentro de una sesión:
[20]:
from qiskit_ibm_runtime import Session, Estimator
with Session(backend=backend, max_time="1h"):
estimator = Estimator()
result = estimator.run(circuit, observable).result()
print(f">>> Expectation value from the first run: {result.values[0]}")
result = estimator.run(circuit, observable).result()
print(f">>> Expectation value from the second run: {result.values[0]}")
>>> Expectation value from the first run: 0.858
>>> Expectation value from the second run: 0.85
Invocar múltiples primitivas en una sesión¶
No estás restringido a una sola función primitiva dentro de una sesión. En esta sección te mostraremos un ejemplo del uso de múltiples primitivas.
Primero preparamos un circuito para la primitiva sampler.
[21]:
from qiskit.circuit.random import random_circuit
sampler_circuit = random_circuit(2, 2, seed=0).decompose(reps=1)
sampler_circuit.measure_all()
display(circuit.draw("mpl"))

El siguiente ejemplo muestra cómo puedes crear tanto una instancia de la clase Sampler
como una de la clase Estimator
e invocar sus métodos run()
dentro de una sesión.
[22]:
from qiskit_ibm_runtime import Session, Sampler, Estimator
with Session(backend=backend):
sampler = Sampler()
estimator = Estimator()
result = sampler.run(sampler_circuit).result()
print(f">>> Quasi-probability distribution from the sampler job: {result.quasi_dists[0]}")
result = estimator.run(circuit, observable).result()
print(f">>> Expectation value from the estimator job: {result.values[0]}")
>>> Quasi-probability distribution from the sampler job: {2: 0.50125, 0: 0.49875}
>>> Expectation value from the estimator job: 0.8525
Las llamadas también pueden ser asíncronas. No necesitas esperar el resultado de un trabajo anterior para enviar otro.
[23]:
from qiskit_ibm_runtime import Session, Sampler, Estimator
with Session(backend=backend):
sampler = Sampler()
estimator = Estimator()
sampler_job = sampler.run(sampler_circuit)
estimator_job = estimator.run(circuit, observable)
print(
f">>> Quasi-probability distribution from the sampler job: {sampler_job.result().quasi_dists[0]}"
)
print(f">>> Expectation value from the estimator job: {estimator_job.result().values[0]}")
>>> Quasi-probability distribution from the sampler job: {2: 0.49025, 0: 0.50975}
>>> Expectation value from the estimator job: 0.8475
Resumen¶
El siguiente código resume el uso de las primitivas, las opciones y la sesión de Qiskit Runtime.
[24]:
from qiskit_ibm_runtime import (
QiskitRuntimeService,
Session,
Sampler,
Estimator,
Options,
)
# 1. Initialize account
service = QiskitRuntimeService(channel="ibm_quantum")
# 2. Specify options, such as enabling error mitigation
options = Options(resilience_level=1)
# 3. Select a backend.
backend = service.backend("ibmq_qasm_simulator")
# 4. Create a session
with Session(backend=backend):
# 5. Create primitive instances
sampler = Sampler(options=options)
estimator = Estimator(options=options)
# 6. Submit jobs
sampler_job = sampler.run(sampler_circuit)
estimator_job = estimator.run(circuit, observable)
# 7. Get results
print(
f">>> Quasi-probability distribution from the sampler job: {sampler_job.result().quasi_dists[0]}"
)
print(f">>> Expectation value from the estimator job: {estimator_job.result().values[0]}")
>>> Quasi-probability distribution from the sampler job: {0: 0.49575, 2: 0.50425}
>>> Expectation value from the estimator job: 0.8315
Referencia¶
Puedes encontrar más detalles sobre los métodos de Estimator
en la referencia de la API de Estimator.
Y todas las opciones disponibles en la referencia de la API de Options.
[25]:
import qiskit_ibm_runtime
qiskit_ibm_runtime.version.get_version_info()
[25]:
'0.8.0'
[26]:
from qiskit.tools.jupyter import *
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.22.2 |
qiskit-aer | 0.11.0 |
qiskit-ibmq-provider | 0.19.2 |
qiskit-nature | 0.5.0 |
System information | |
Python version | 3.8.1 |
Python compiler | Clang 11.0.3 (clang-1103.0.32.62) |
Python build | default, Jul 15 2020 18:48:27 |
OS | Darwin |
CPUs | 8 |
Memory (Gb) | 16.0 |
Mon Nov 07 21:11:54 2022 EST |
This code is a part of Qiskit
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.