Asistente de aprendizaje de ruido
Package versions
El código de esta página fue desarrollado usando los siguientes requisitos. Recomendamos usar estas versiones o posteriores.
qiskit[all]~=2.4.1
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.18.0
Las técnicas de mitigación de errores PEA y PEC utilizan un componente de aprendizaje de ruido basado en un modelo de ruido de Pauli-Lindblad, que normalmente se gestiona durante la ejecución después de enviar uno o más trabajos a través de qiskit-ibm-runtime sin acceso local al modelo de ruido ajustado. Sin embargo, a partir de qiskit-ibm-runtime v0.27.1, se han creado las clases NoiseLearner y NoiseLearnerOptions para obtener los resultados de estos experimentos de aprendizaje de ruido. Estos resultados pueden almacenarse localmente como un NoiseLearnerResult y usarse como entrada en experimentos posteriores. Esta página ofrece una descripción general de su uso y las opciones disponibles.
Además, a partir de qiskit-ibm-runtime v0.47.0, existe una nueva clase NoiseLearnerV3 compatible con la primitiva Executor. Esta nueva versión, también parte del modelo de ejecución dirigida, te permite especificar explícitamente las capas que deseas aprender.
NoiseLearner solo funciona con EstimatorV2 y NoiseLearnerV3 solo funciona con Executor.
NoiseLearner
Descripción general
La clase NoiseLearner realiza experimentos que caracterizan los procesos de ruido basándose en un modelo de ruido de Pauli-Lindblad para uno o más circuitos. Dispone de un método run() que ejecuta los experimentos de aprendizaje y acepta como entrada una lista de circuitos o un PUB; devuelve un NoiseLearnerResult que contiene los canales de ruido aprendidos y metadatos sobre los trabajos enviados. A continuación se muestra un fragmento de código que demuestra el uso del programa asistente.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import (
NoiseLearnerOptions,
ResilienceOptionsV2,
EstimatorOptions,
)
# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]
circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
circuit.cx(pair[0], pair[1])
for pair in odd_edges:
circuit.cx(pair[0], pair[1])
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_learn = pm.run(circuit)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend)
job = learner.run([circuit_to_learn])
noise_model = job.result()
El NoiseLearnerResult.data resultante es una lista de objetos LayerError que contienen el modelo de ruido para cada capa de entrelazamiento individual que pertenece a los circuitos objetivo. Cada LayerError almacena la información de la capa, en forma de circuito y un conjunto de etiquetas de qubits, junto con el PauliLindbladError para el modelo de ruido aprendido para la capa dada.
import numpy
print(
f"Noise learner result contains {len(noise_model.data)} entries"
f" and has the following type:\n {type(noise_model)}\n"
)
print(
f"Each element of `NoiseLearnerResult` then contains"
f" an object of type:\n {type(noise_model.data[0])}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"And each of these `LayerError` objects possess"
f" data on the generators for the error channel: \n{noise_model.data[0].error.generators}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"Along with the error rates: \n{noise_model.data[0].error.rates}\n"
)
Noise learner result contains 2 entries and has the following type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>
Each element of `NoiseLearnerResult` then contains an object of type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>
And each of these `LayerError` objects possess data on the generators for the error channel:
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
'IIIIIIIIIIIIIIIIIIIIIIIIXIX', 'IIIIIIIIIIIIIIIIIIIIIIIIXIY',
'IIIIIIIIIIIIIIIIIIIIIIIIXIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYIX', 'IIIIIIIIIIIIIIIIIIIIIIIIYIY',
'IIIIIIIIIIIIIIIIIIIIIIIIYIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZIX', 'IIIIIIIIIIIIIIIIIIIIIIIIZIY',
'IIIIIIIIIIIIIIIIIIIIIIIIZIZ', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIYIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'IIIIIIIIIIIIIIIIIIIIIIXIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIII',
'IIIIIIIIIIIIIIIIIIIIIIXYIII', 'IIIIIIIIIIIIIIIIIIIIIIXZIII',
'IIIIIIIIIIIIIIIIIIIIIIYIIII', 'IIIIIIIIIIIIIIIIIIIIIIYXIII',
'IIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIYZIII',
'IIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIZXIII',
'IIIIIIIIIIIIIIIIIIIIIIZYIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIII',
'IIIIIIIIIIIIIIIIIIIIIXIIIII', 'IIIIIIIIIIIIIIIIIIIIIXXIIII',
'IIIIIIIIIIIIIIIIIIIIIXYIIII', 'IIIIIIIIIIIIIIIIIIIIIXZIIII',
'IIIIIIIIIIIIIIIIIIIIIYIIIII', 'IIIIIIIIIIIIIIIIIIIIIYXIIII',
'IIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIYZIIII',
'IIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIZXIIII',
'IIIIIIIIIIIIIIIIIIIIIZYIIII', 'IIIIIIIIIIIIIIIIIIIIIZZIIII',
'IIIIIIIIIIIIIIIIIIIIXIIIIII', 'IIIIIIIIIIIIIIIIIIIIXXIIIII',
'IIIIIIIIIIIIIIIIIIIIXYIIIII', 'IIIIIIIIIIIIIIIIIIIIXZIIIII',
'IIIIIIIIIIIIIIIIIIIIYIIIIII', 'IIIIIIIIIIIIIIIIIIIIYXIIIII',
'IIIIIIIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIIIIIIYZIIIII',
'IIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIZXIIIII',
'IIIIIIIIIIIIIIIIIIIIZYIIIII', 'IIIIIIIIIIIIIIIIIIIIZZIIIII',
'IIIIIIIIIIIIIIIIIIIXIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIII',
'IIIIIIIIIIIIIIIIIIIXYIIIIII', 'IIIIIIIIIIIIIIIIIIIXZIIIIII',
'IIIIIIIIIIIIIIIIIIIYIIIIIII', 'IIIIIIIIIIIIIIIIIIIYXIIIIII',
'IIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIYZIIIIII', ...]
Along with the error rates:
[5.9e-04 5.3e-04 5.7e-04 ... 0.0e+00 1.0e-05 0.0e+00]
El atributo LayerError.error del resultado del aprendizaje de ruido contiene los generadores y las tasas de error del modelo de Pauli-Lindblad ajustado, que tiene la forma
donde los son los LayerError.rates y los son los operadores de Pauli especificados en LayerError.generators.
Opciones de aprendizaje de ruido
Puedes elegir entre varias opciones al instanciar un objeto NoiseLearner. Estas opciones están encapsuladas por la clase qiskit_ibm_runtime.options.NoiseLearnerOptions e incluyen la posibilidad de especificar el número máximo de capas a aprender, el número de aleatorizaciones y la estrategia de twirling, entre otras. Consulta la documentación de la API de NoiseLearnerOptions para obtener información detallada.
A continuación se muestra un ejemplo sencillo que ilustra cómo usar NoiseLearnerOptions en un experimento con NoiseLearner:
# Build a GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_run = pm.run(circuit_to_learn)
# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend, options=learner_options)
job = learner.run([circuit_to_run])
noise_model = job.result()
Pasar el modelo de ruido a una primitiva
El modelo de ruido aprendido sobre el circuito también puede usarse como entrada para la primitiva EstimatorV2 implementada en Qiskit Runtime. Este puede pasarse a la primitiva de varias formas distintas. Los siguientes tres ejemplos muestran cómo puedes pasar el modelo de ruido al atributo estimator.options directamente, usando un objeto ResilienceOptionsV2 antes de instanciar una primitiva Estimator, y pasando un diccionario con el formato adecuado.
# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model
# Specify options through a ResilienceOptionsV2 object
resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)
estimator_options = EstimatorOptions(resilience=resilience_options)
estimator = EstimatorV2(mode=backend, options=estimator_options)
# Specify options by using a dictionary
options_dict = {
"resilience_level": 2,
"resilience": {"layer_noise_model": noise_model},
}
estimator = EstimatorV2(mode=backend, options=options_dict)
Después de pasar el modelo de ruido al objeto EstimatorV2, puede usarse para ejecutar cargas de trabajo y realizar la mitigación de errores de forma normal.
NoiseLearnerV3
Descripción general
De manera similar a NoiseLearner, la clase NoiseLearnerV3 realiza experimentos que caracterizan los procesos de ruido basándose en un modelo de ruido de Pauli-Lindblad para uno o más circuitos. Su método run() acepta una lista de instrucciones, cada una de las cuales debe ser un BoxOp con anotaciones de twirling que contenga operaciones ISA.
El resultado de un trabajo NoiseLearnerV3 contiene una lista de objetos NoiseLearnerV3Result, uno por cada instrucción de entrada.
El siguiente código muestra cómo usar el programa asistente.
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions
# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]
circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
circuit.cx(pair[0], pair[1])
for pair in odd_edges:
circuit.cx(pair[0], pair[1])
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit = pm.run(circuit)
# Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)
# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)
print(f"Found {len(unique_box_instructions)} unique layers")
print(
f"Each instruction is of type {type(unique_box_instructions[0].operation)}"
)
print(
f"And has annotations: {unique_box_instructions[0].operation.annotations}"
)
# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()
Found 3 unique layers
Each instruction is of type <class 'qiskit.circuit.controlflow.box.BoxOp'>
And has annotations: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='', site='before')]
El resultado del trabajo es una lista de objetos NoiseLearnerV3Result, uno por cada conjunto de instrucciones encuadradas de entrada. NoiseLearnerV3Result tiene un método to_pauli_lindblad_map() que devuelve un objeto PauliLindbladMap, el cual tiene métodos para extraer los generadores, las tasas de error y más.
print(
f"The Noise learner V3 result contains {len(learner_result)} entries"
f" and each has the following type:\n {type(learner_result[0])}\n"
)
noise_map = learner_result[0].to_pauli_lindblad_map()
print(
f"After converting to PauliLindbladMap, you can extract data "
f" on the generators for the error channel (truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
print(
f"Along with the error rates (truncated to 3): \n{noise_map.rates[:3]}\n"
)
The Noise learner V3 result contains 3 entries and each has the following type:
<class 'qiskit_ibm_runtime.results.noise_learner_v3.NoiseLearnerV3Result'>
After converting to PauliLindbladMap, you can extract data on the generators for the error channel (truncated to 3):
<QubitSparsePauliList with 3 elements on 27 qubits: [X_0, Y_0, Z_0]>
Along with the error rates (truncated to 3):
[0.00026 0.00032 0.00023]
Opciones de aprendizaje de ruido
NoiseLearnerV3 admite varias opciones, incluido el número de aleatorizaciones y la profundidad de pares de capas, entre otras. De manera similar a las primitivas, puedes especificar las opciones durante o después de instanciar el objeto NoiseLearnerV3. El ejemplo de código anterior muestra cómo establecer las opciones shots_per_randomization y num_randomizations. Consulta la documentación de la API de NoiseLearnerV3Options para obtener información detallada.
Pasar un modelo de ruido a Executor
Executor sigue los objetivos de diseño especificados en las anotaciones del circuito (en forma de samplex) y las opciones. InjectNoise es la anotación para especificar dónde inyectar el ruido, y el argumento pauli_lindblad_maps del samplex especifica qué mapa de ruido usar.
El circuito del ejemplo anterior se procesa mediante el boxing pass manager, que agrupa las instrucciones en cajas anotadas. El código relevante se incluye aquí para facilitar la comprensión.
inject_noise_targets="gates"especifica que se añadan las anotacionesInjectNoisea las cajas que contienen operaciones de entrelazamiento.inject_noise_strategy="uniform_modification"especifica que se asigne el mismorefymodifier_refa todas las cajas equivalentes con anotacionesInjectNoise.InjectNoise.refes un identificador único que se usa para asignar un modelo de ruido a esa caja.InjectNoise.modifier_refpermite escalar el modelo de ruido asignado a una caja mediante factores multiplicativos.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
El circuito del ejemplo anterior contiene tres cajas, dos de las cuales tienen anotaciones InjectNoise con atributos ref diferentes (ya que no son equivalentes).
# box_circuit comes from the example above
for idx, instruction in enumerate(boxed_circuit):
# The `InjectNoise` annotation defines which boxes to inject noise.
print(f"Annotations of box #{idx}: {instruction.operation.annotations}\n")
Annotations of box #0: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='r789B', site='before')]
Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r054B', modifier_ref='r054B', site='before')]
Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]
El resultado del trabajo NoiseLearnerV3 debe convertirse en un diccionario antes de pasarse a Executor. Las claves de este diccionario son los atributos InjectNoise.ref y los valores son los mapas de ruido correspondientes. Este mapeo le indica a Executor qué modelos de ruido inyectar y dónde.
El siguiente código muestra cómo tomar el circuito y el resultado de NoiseLearnerV3 del ejemplo anterior y pasarlos a Executor, que generará las variantes del circuito con los modelos de ruido inyectados y las ejecutará en hardware.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic import build
# Generate a quantum program
program = QuantumProgram(shots=1000)
# Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)
# Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)
# Append the samplex item and execute
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)
executor = Executor(backend)
executor_job = executor.run(program)
Próximos pasos
- Consulta la referencia de la API de EstimatorOptions y la referencia de la API de ResilienceOptionsV2.
- Aprende más sobre las técnicas de mitigación y supresión de errores disponibles en Qiskit Runtime.
- Aprende a implementar la gestión de ruido del Estimator.
- Lee Migrar a primitivas V2.