Spanish
Idiomas
English
Japanese
Spanish

Nota

Esta página fue generada a partir de docs/tutorials/how-to-getting-started-with-sampler.ipynb.

Primeros pasos con la primitiva Sampler

En este tutorial, te mostraremos cómo configurar la primitiva Sampler 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 Sampler. Hay un tutorial separado sobre los Primeros pasos con la primitiva Estimator.

Utilizar la primitiva Sampler

Similar a la clase base Backend, hay una clase base Sampler definida en Qiskit Terra que estandariza la forma en que los usuarios interactúan con todas las implementaciones de Sampler. 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

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, measure=True).decompose(reps=1)
display(circuit.draw("mpl"))
../_images/tutorials_how-to-getting-started-with-sampler_9_0.png

2. Inicializar una clase Sampler

The next step is to create an instance of an Sampler class, which can be any of the subclasses that comply with the base specification. For simplicity, we will use Qiskit Terra’s qiskit.primitives.Sampler class, based on the Statevector construct (that is, algebraic simulation).

[2]:
from qiskit.primitives import Sampler

sampler = Sampler()

3. Invocar el Sampler y obtener resultados

Para estimar la distribución de cuasi-probabilidad de la salida del circuito, invoca al método run() de la instancia Sampler que acabas de crear y pasa el circuito como un parámetro 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().

[3]:
job = sampler.run(circuit)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: 979495e7-7f0d-4b92-acbc-19da7dad864d
>>> Job Status: JobStatus.DONE

The result() method of the job will return the SamplerResult, which includes both the quasi-probability distribution and job metadata.

[4]:
result = job.result()
print(f">>> {result}")
print(f"  > Quasi-probability distribution: {result.quasi_dists[0]}")
>>> SamplerResult(quasi_dists=[{0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}], metadata=[{}])
  > Quasi-probability distribution: {0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}

Puedes seguir invocando al método run() nuevamente con las diferentes entradas:

[5]:
circuit = random_circuit(2, 2, seed=1, measure=True).decompose(reps=1)

job = sampler.run(circuit)
result = job.result()

display(circuit.draw("mpl"))
print(f">>> Quasi-probability distribution: {result.quasi_dists[0]}")
../_images/tutorials_how-to-getting-started-with-sampler_19_0.png
>>> Quasi-probability distribution: {0: 0.9999999999999991, 1: 6.580329297619248e-33, 2: 0.0, 3: 0.0}

También puedes proporcionar entradas compuestas al método run():

[6]:
circuits = (
    random_circuit(2, 2, seed=0, measure=True).decompose(reps=1),
    random_circuit(2, 2, seed=1, measure=True).decompose(reps=1),
)

job = sampler.run(circuits)
result = job.result()

[display(cir.draw("mpl")) for cir in circuits]
print(f">>> Quasi-probability distribution: {result.quasi_dists}")
../_images/tutorials_how-to-getting-started-with-sampler_21_0.png
../_images/tutorials_how-to-getting-started-with-sampler_21_1.png
>>> Quasi-probability distribution: [{0: 0.4999999999999999, 1: 0.0, 2: 0.4999999999999998, 3: 0.0}, {0: 0.9999999999999991, 1: 6.580329297619248e-33, 2: 0.0, 3: 0.0}]

O usar circuitos parametrizados:

[7]:
from qiskit.circuit.library import RealAmplitudes

circuit = RealAmplitudes(num_qubits=2, reps=2).decompose(reps=1)
circuit.measure_all()
parameter_values = [0, 1, 2, 3, 4, 5]

job = sampler.run(circuit, parameter_values)
result = job.result()

display(circuit.draw("mpl"))
print(f">>> Parameter values: {parameter_values}")
print(f">>> Quasi-probability distribution: {result.quasi_dists[0]}")
../_images/tutorials_how-to-getting-started-with-sampler_23_0.png
>>> Parameter values: [0, 1, 2, 3, 4, 5]
>>> Quasi-probability distribution: {0: 0.17158451004815306, 1: 0.0041370682135240654, 2: 0.20402129418492707, 3: 0.6202571275533961}

Usar Sampler de Qiskit Runtime

En esta sección, veremos cómo usar la implementación de Qiskit Runtime de la primitiva Sampler.

1. Inicializar la cuenta

Dado que Sampler 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.

[8]:
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService(channel="ibm_quantum")
backend = service.backend("ibmq_qasm_simulator")

2. Crear un circuito

Al igual que en la sección anterior, necesitarás al menos un circuito como la entrada para la primitiva Sampler.

[9]:
from qiskit.circuit.random import random_circuit

circuit = random_circuit(2, 2, seed=0, measure=True).decompose(reps=1)
display(circuit.draw("mpl"))
../_images/tutorials_how-to-getting-started-with-sampler_31_0.png

3. Inicializar Sampler de Qiskit Runtime

Aquí estamos inicializando una instancia de qiskit_ibm_runtime.Sampler en lugar de qiskit.primitives.Sampler para usar la implementación de Sampler de Qiskit Runtime.

When you initialize the Sampler, you’ll need to pass in the backend you previously selected as the target device (or simulator), using the backend parameter.

[10]:
from qiskit_ibm_runtime import Sampler

sampler = Sampler(backend=backend)

4. Invocar el Sampler y obtener resultados

You can then invoke the run() method to calculate expectation values for the input circuit(s) and observable(s).

[11]:
job = sampler.run(circuit)
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: cdkrk4qan60ka16e6v0g
>>> Job Status: JobStatus.RUNNING
[12]:
result = job.result()
print(f">>> {result}")
print(f"  > Quasi-probability distribution: {result.quasi_dists[0]}")
print(f"  > Metadata: {result.metadata[0]}")
>>> SamplerResult(quasi_dists=[{2: 0.49275, 0: 0.50725}], metadata=[{'header_metadata': {}, 'shots': 4000}])
  > Quasi-probability distribution: {2: 0.49275, 0: 0.50725}
  > Metadata: {'header_metadata': {}, '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.

options

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.

[13]:
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.

[14]:
from qiskit_ibm_runtime import Options

options = Options()
options.resilience_level = 1
options.execution.shots = 2048

Al crear una instancia de la clase Sampler, puedes pasar las options que acabas de crear. Esas opciones se aplicarán cuando uses run() para realizar el cálculo.

[15]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'header_metadata': {}, 'shots': 2048, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.028210751246660948}

También puedes pasar opciones a través del método run(). Esto sobrescribirá las opciones que especificaste al crear la instancia de Sampler 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).

[16]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit, shots=1024).result()
print(f">>> Metadata: {result.metadata[0]}")
>>> Metadata: {'header_metadata': {}, 'shots': 1024, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.002864845097064972}

Supresión y mitigación de errores

optimization_level y resilience_level se utilizan para configurar la supresión y mitigación de errores.

Sampler admite optimization_level 0-3 y resilience_level 0-1.

[17]:
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)
[18]:
sampler = Sampler(backend=backend, options=options)
result = sampler.run(circuit).result()
print(f">>> Quasi-probability distribution: {result.quasi_dists[0]}")
print(f">>> Metadata: {result.metadata[0]}")
>>> Quasi-probability distribution: {0: 0.50175, 2: 0.49825}
>>> Metadata: {'header_metadata': {}, 'shots': 4000, 'readout_mitigation_overhead': 1.0, 'readout_mitigation_time': 0.04642554186284542}

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. Siempre que 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:

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 Sampler.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 Sampler.run una o más veces dentro de una sesión:

[19]:
from qiskit_ibm_runtime import Session, Estimator

with Session(backend=backend, max_time="1h"):
    sampler = Sampler()

    result = sampler.run(circuit).result()
    print(f">>> Quasi-probability distribution from the first run: {result.quasi_dists[0]}")

    result = sampler.run(circuit).result()
    print(f">>> Quasi-probability distribution from the second run: {result.quasi_dists[0]}")
>>> Quasi-probability distribution from the first run: {0: 0.498, 2: 0.502}
>>> Quasi-probability distribution from the second run: {0: 0.498, 2: 0.502}

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 y un observable para la primitiva Estimator.

[20]:
from qiskit.circuit.random import random_circuit
from qiskit.quantum_info import SparsePauliOp

estimator_circuit = random_circuit(2, 2, seed=0).decompose(reps=1)
display(estimator_circuit.draw("mpl"))

observable = SparsePauliOp("XZ")
print(f">>> Observable: {observable.paulis}")
../_images/tutorials_how-to-getting-started-with-sampler_65_0.png
>>> Observable: ['XZ']

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.

[21]:
from qiskit_ibm_runtime import Session, Sampler, Estimator

with Session(backend=backend):
    sampler = Sampler()
    estimator = Estimator()

    result = sampler.run(circuit).result()
    print(f">>> Quasi-probability distribution from the sampler job: {result.quasi_dists[0]}")

    result = estimator.run(estimator_circuit, observable).result()
    print(f">>> Expectation value from the estimator job: {result.values[0]}")
>>> Quasi-probability distribution from the sampler job: {2: 0.50025, 0: 0.49975}
>>> Expectation value from the estimator job: 0.848

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(circuit)
    estimator_job = estimator.run(estimator_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.508, 0: 0.492}
>>> Expectation value from the estimator job: 0.8495

Resumen

El siguiente código resume rápidamente el uso de las primitivas, las opciones y las sesiones de Qiskit Runtime.

[25]:
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(circuit)
    estimator_job = estimator.run(estimator_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.50125, 2: 0.49875}
>>> Expectation value from the estimator job: 0.8475

Referencia

Puedes encontrar más detalles sobre los métodos de Sampler en la referencia de la API de Sampler.

Y todas las opciones disponibles en la referencia de la API de Options.

[26]:
import qiskit_ibm_runtime

qiskit_ibm_runtime.version.get_version_info()
[26]:
'0.8.0'
[27]:
from qiskit.tools.jupyter import *

%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.2
qiskit-aer0.11.0
qiskit-ibmq-provider0.19.2
qiskit-nature0.5.0
System information
Python version3.8.1
Python compilerClang 11.0.3 (clang-1103.0.32.62)
Python builddefault, Jul 15 2020 18:48:27
OSDarwin
CPUs8
Memory (Gb)16.0
Mon Nov 07 21:10:31 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.