Spanish
Idiomas
English
Japanese
Spanish

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')
../_images/noisy-sim-circuit.png

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>
../_images/noisy-sim-sampler-ideal.png

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')
../_images/noisy-sim-estimator-circuit.png
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.

\[\langle ZZ\rangle =\langle \psi | ZZ | \psi\rangle=\langle \psi|(|0\rangle\langle 0| -|1\rangle\langle 1|)\otimes(|0\rangle\langle 0| - |1\rangle\langle 1|) |\psi\rangle =|\langle 00|\psi\rangle|^2 - |\langle 01 | \psi\rangle|^2 - |\langle 10 | \psi\rangle|^2 + |\langle 11|\psi\rangle|^2\]

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>
../_images/noisy-sim-estimator-ideal.png

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>
../_images/noisy-sim-sampler-noisy.png

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>
../_images/noisy-sim-estimator-noisy.png

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 SoftwareVersion
qiskit-terra0.22.2
qiskit-aer0.11.1
qiskit-ibmq-provider0.19.2
qiskit0.39.2
qiskit-nature0.5.0
qiskit-finance0.3.4
qiskit-optimization0.4.0
qiskit-machine-learning0.5.0
System information
Python version3.8.13
Python compilerGCC 10.3.0
Python builddefault, Mar 25 2022 06:04:10
OSLinux
CPUs8
Memory (Gb)31.211326599121094
Wed Nov 30 02:43:41 2022 UTC