Simuladores ruidosos en Qiskit Runtime¶
Este cuaderno muestra cómo configurar ibmq_qasm_simulator
y mapear un modelo de ruido básico para un dispositivo de hardware IBM Quantum en Qiskit Runtime, y usar este modelo de ruido para realizar simulaciones ruidosas de QuantumCircuits
usando Sampler
y Estimator
para estudiar los efectos de los errores que se producen en dispositivos reales.
Configurar tu entorno de desarrollo local¶
Este tutorial requiere que se configure una instancia del servicio Qiskit Runtime. Si aún no lo has hecho, sigue estos pasos para configurar uno.
# load necessary Runtime libraries
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Estimator, Session, Options
service = QiskitRuntimeService(channel="ibm_quantum")
Preparar el entorno¶
Para demostrar la rutina, procederemos a ejecutar una rutina de ejemplo. Uno de los principales beneficios del uso de primitivas es la simplificación de vincular múltiples parámetros en circuitos parametrizados. Para verificar esto, aquí hay un circuito de ejemplo con una compuerta P controlada como se implementa en el siguiente código. Aquí, parametrizamos la P-gate
con un parámetro de rotación theta
. Para aprender a crear circuitos y vincularles parámetros usando Qiskit, consulta Aspectos Básicos de un Circuito y Circuitos Avanzados en la documentación de Qiskit.
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit
theta = Parameter('theta')
qc = QuantumCircuit(2,1)
qc.x(1)
qc.h(0)
qc.cp(theta,0,1)
qc.h(0)
qc.measure(0,0)
qc.draw('mpl')

El circuito que se muestra en la celda anterior está parametrizado con el valor propio que se devuelve (kickback) al qubit 0 a ser medido. La cantidad de retroceso (kickback) estará determinada por el parámetro theta. Ahora, en la siguiente celda, definiremos nuestros parámetros para nuestro circuito como una lista. Los parámetros aquí serán de \(0\) a \(2\pi\) divididos en 50 puntos espaciados uniformemente.
import numpy as np
phases = np.linspace(0, 2*np.pi, 50)
# phases need to be expressed as a list of lists in order to work
individual_phases = [[phase] for phase in phases]
Ejecutar en el simulador ideal¶
Establecer el backend y las opciones a usar¶
Primero demostraremos una ejecución usando un caso ideal sin ningún noise_model
, optimization_level
o resilience_level
tanto para Sampler como para Estimator. Procederemos a configurar las opciones en el siguiente código:
backend = "ibmq_qasm_simulator" # use the simulator
options = Options()
options.simulator.seed_simulator = 42
options.execution.shots = 1000
options.optimization_level = 0 # no optimization
options.resilience_level = 0 # no error mitigation
Ejecutar los circuitos en Sampler¶
Ahora probaremos el circuito para obtener el resultado de la distribución de probabilidad utilizando la primitiva Sampler para hacer lo mismo. Para aprender a usar la primitiva Sampler
y cómo comenzar a usar las Sesiones de Qiskit Runtime, puedes consultar este tutorial: Primeros pasos con la primitiva Sampler.
with Session(service=service, backend=backend):
sampler = Sampler(options=options)
job = sampler.run(
circuits=[qc]*len(phases),
parameter_values=individual_phases
)
result = job.result()
import matplotlib.pyplot as plt
# the probablity of being in the 1 state for each of these values
prob_values = [dist.get(1, 0) for dist in result.quasi_dists]
plt.plot(phases, prob_values, 'o', label='Simulator')
plt.plot(phases, np.sin(phases/2,)**2, label='Theory')
plt.xlabel('Phase')
plt.ylabel('Probability')
plt.legend()
<matplotlib.legend.Legend at 0x7f7fd233b6d0>

Ejecutar los circuitos en Estimator¶
Para aprender cómo iniciar una sesión para Estimator, puedes consultar este tutorial: Primeros pasos con la primitiva Estimator.
El Estimator vinculará las rotaciones de un solo qubit para obtener Hamiltonianos antes de devolver los valores esperados de los operadores cuánticos. Por lo tanto, el circuito no requiere ninguna medición. Actualmente, el circuito qc
tiene mediciones, por lo que las eliminaremos con remove_final_measurements
.
qc_no_meas = qc.remove_final_measurements(inplace=False)
qc_no_meas.draw('mpl')

from qiskit.quantum_info import SparsePauliOp
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
print(f" > Observable: {ZZ.paulis}")
> Observable: ['ZZ']
Con este observable, el valor esperado se calcula mediante la siguiente ecuación.
La siguiente celda implementará esto como se muestra.
with Session(service=service, backend=backend):
estimator = Estimator(options=options)
job = estimator.run(
circuits=[qc_no_meas]*len(phases),
parameter_values=individual_phases,
observables=[ZZ]*len(phases)
)
result = job.result()
exp_values = result.values
plt.plot(phases, exp_values, 'o', label='Simulator')
plt.plot(phases, 2*np.sin(phases/2)**2-1, label='Theory')
plt.xlabel('Phase')
plt.ylabel('Expectation')
plt.legend()
<matplotlib.legend.Legend at 0x7f7fd0ed8820>

Ejecutar una simulación ruidosa¶
Ahora configuraremos nuestro simulador para ejecutar una simulación ruidosa en lugar de la ideal. Podemos pasar un noise_model
personalizado al simulador en Runtime especificándolo en el parámetro Options
. Aquí intentaremos imitar un backend real y mapear en el noise_model
de una clase FakeBackend
. El modelo de ruido se puede extraer de FakeBackend
y pasarlo como un parámetro simulator
en las opciones. Si deseas obtener más información sobre fake_provider
, consulta Fake Provider en la documentación de Qiskit.
Dado que estamos tratando de imitar un backend real, también podemos pasar el coupling_map
que tiene la topología del backend y las basis_gates
que admite el backend para tener una simulación ruidosa más realista.
from qiskit.providers.fake_provider import FakeManila
from qiskit_aer.noise import NoiseModel
# Make a noise model
fake_backend = FakeManila()
noise_model = NoiseModel.from_backend(fake_backend)
# Set options to include the noise model
options = Options()
options.simulator = {
"noise_model": noise_model,
"basis_gates": fake_backend.configuration().basis_gates,
"coupling_map": fake_backend.configuration().coupling_map,
"seed_simulator": 42
}
# Set number of shots, optimization_level and resilience_level
options.execution.shots = 1000
options.optimization_level = 0
options.resilience_level = 0
set_backend()
is the syntactic sugar for setting options.
The following code is equivalent.
from qiskit.providers.fake_provider import FakeManila
# Make a noise model
fake_backend = FakeManila()
# Set options to include the noise model
options = Options()
options.simulator.set_backend(fake_backend)
options.simulator.seed_simulator = 42
# Set number of shots, optimization_level and resilience_level
options.execution.shots = 1000
options.optimization_level = 0
options.resilience_level = 0
El ibmq_qasm_simulator
permite la activación de resilience_levels
ofrecidos por el Servicio Qiskit Runtime, y el uso de estos niveles en simuladores se demuestra mejor usando la simulación ruidosa como se ha descrito anteriormente.
Para ver la comparación, definiremos dos conjuntos de Options
. El ibmq_qasm_simulator
permite la activación de los niveles de resiliencia ofrecidos por Qiskit Runtime, y el uso de estos niveles en simuladores se demuestra mejor mediante la simulación ruidosa que hemos creado. Aquí, options
se establece enresilience level
= 0
para representar una ejecución normal sin mitigación de errores, y options with em
se establece en resilience level
= 1
para representar una ejecución con la mitigación de errores habilitada.
# Set options to include the noise model with error mitigation
options_with_em = Options()
options_with_em.simulator = {
"noise_model": noise_model,
"basis_gates": fake_backend.configuration().basis_gates,
"coupling_map": fake_backend.configuration().coupling_map,
"seed_simulator": 42
}
# Set number of shots, optimization_level and resilience_level
options_with_em.execution.shots = 1000
options_with_em.optimization_level = 0 # no optimization
options_with_em.resilience_level = 1 # M3 for Sampler and T-REx for Estimator
Cuando estableces resilience_level
en 1, M3 se activa en Sampler. Todas las configuraciones de nivel de resiliencia disponibles se pueden encontrar aquí.
with Session(service=service, backend=backend):
# include the noise model without M3
sampler = Sampler(options=options)
job = sampler.run(
circuits=[qc]*len(phases),
parameter_values=individual_phases
)
result = job.result()
prob_values = [1-dist[0] for dist in result.quasi_dists]
# include the noise model with M3
sampler = Sampler(options=options_with_em)
job = sampler.run(
circuits=[qc]*len(phases),
parameter_values=individual_phases
)
result = job.result()
prob_values_with_em = [1-dist[0] for dist in result.quasi_dists]
plt.plot(phases, prob_values, 'o', label='Noisy')
plt.plot(phases, prob_values_with_em, 'o', label='Mitigated')
plt.plot(phases, np.sin(phases/2,)**2, label='Theory')
plt.xlabel('Phase')
plt.ylabel('Probability')
plt.legend()
<matplotlib.legend.Legend at 0x7f7fb4230700>

T-REx
se activa en Estimator cuando el nivel de resiliencia se establece en 1.
with Session(service=service, backend=backend):
# include the noise model without T-REx
estimator = Estimator(options=options)
job = estimator.run(
circuits=[qc_no_meas]*len(phases),
parameter_values=individual_phases,
observables=[ZZ]*len(phases)
)
result = job.result()
exp_values = result.values
# include the noise model with T-REx
estimator = Estimator(options=options_with_em)
job = estimator.run(
circuits=[qc_no_meas]*len(phases),
parameter_values=individual_phases,
observables=[ZZ]*len(phases))
result = job.result()
exp_values_with_em = result.values
plt.plot(phases, exp_values, 'o', label='Noisy')
plt.plot(phases, exp_values_with_em, 'o', label='Mitigated')
plt.plot(phases, 2*np.sin(phases/2)**2-1, label='Theory')
plt.xlabel('Phase')
plt.ylabel('Expectation')
plt.legend()
<matplotlib.legend.Legend at 0x7f7f7006ca00>

Los niveles de resiliencia se encuentran actualmente en versión beta, por lo que la sobrecarga de muestreo y la calidad de la solución variarán de un circuito a otro. Se lanzarán nuevas funciones, opciones avanzadas y herramientas de gestión de forma continua. También puedes jugar con niveles más altos de resiliencia y explorar opciones adicionales que ofrecen. Si deseas obtener más información sobre la activación de funciones como Digital-ZNE, PEC además de M3 y T-REx como se muestra en los ejemplos anteriores, consulta este tutorial: Supresión y mitigación de errores con Qiskit Runtime.
import qiskit_ibm_runtime
qiskit_ibm_runtime.version.get_version_info()
'0.8.0'
from qiskit.tools.jupyter import *
%qiskit_version_table
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.22.2 |
qiskit-aer | 0.11.1 |
qiskit-ibmq-provider | 0.19.2 |
qiskit | 0.39.2 |
qiskit-nature | 0.5.0 |
qiskit-finance | 0.3.4 |
qiskit-optimization | 0.4.0 |
qiskit-machine-learning | 0.5.0 |
System information | |
Python version | 3.8.13 |
Python compiler | GCC 10.3.0 |
Python build | default, Mar 25 2022 06:04:10 |
OS | Linux |
CPUs | 8 |
Memory (Gb) | 31.211326599121094 |
Wed Nov 30 02:43:41 2022 UTC |