Asistente de aprendizaje de ruido
Versiones de los paquetes
El código de esta página se desarrolló usando los siguientes requisitos. Recomendamos usar estas versiones o más recientes.
qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.21.0
Las técnicas de mitigación de errores PEA y PEC utilizan ambas 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 ningún acceso local al modelo de ruido ajustado. Sin embargo, a partir de qiskit-ibm-runtime v0.27.1, se han creado una clase NoiseLearner y la correspondiente NoiseLearnerOptions para obtener los resultados de estos experimentos de aprendizaje de ruido. Estos resultados se pueden almacenar luego localmente como un NoiseLearnerResult y usarse como entrada en experimentos posteriores. Esta página ofrece una visión general de su uso y de las opciones asociadas 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 del lado del servidor y NoiseLearnerV3 solo funciona con Executor y el nuevo Estimator del lado del cliente (introducido en qiskit-ibm-runtime v0.50.0).
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 — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "samplomatic": "samplomatic"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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"
f"{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',
'IIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIXYI',
'IIIIIIIIIIIIIIIIIIIIIIIIXZI', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYXI', 'IIIIIIIIIIIIIIIIIIIIIIIIYYI',
'IIIIIIIIIIIIIIIIIIIIIIIIYZI', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZXI', 'IIIIIIIIIIIIIIIIIIIIIIIIZYI',
'IIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIXYII',
'IIIIIIIIIIIIIIIIIIIIIIIXZII', 'IIIIIIIIIIIIIIIIIIIIIIIYIII',
'IIIIIIIIIIIIIIIIIIIIIIIYXII', 'IIIIIIIIIIIIIIIIIIIIIIIYYII',
'IIIIIIIIIIIIIIIIIIIIIIIYZII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'IIIIIIIIIIIIIIIIIIIIIIIZXII', 'IIIIIIIIIIIIIIIIIIIIIIIZYII',
'IIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIXIIII',
'IIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIXYIII',
'IIIIIIIIIIIIIIIIIIIIIIXZIII', 'IIIIIIIIIIIIIIIIIIIIIIYIIII',
'IIIIIIIIIIIIIIIIIIIIIIYXIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIII',
'IIIIIIIIIIIIIIIIIIIIIIYZIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIII',
'IIIIIIIIIIIIIIIIIIIIIIZXIII', 'IIIIIIIIIIIIIIIIIIIIIIZYIII',
'IIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIXIIIII',
'IIIIIIIIIIIIIIIIIIIIIXIIIIX', 'IIIIIIIIIIIIIIIIIIIIIXIIIIY',
'IIIIIIIIIIIIIIIIIIIIIXIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',
'IIIIIIIIIIIIIIIIIIIIIYIIIIX', 'IIIIIIIIIIIIIIIIIIIIIYIIIIY',
'IIIIIIIIIIIIIIIIIIIIIYIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIZIIIII',
'IIIIIIIIIIIIIIIIIIIIIZIIIIX', 'IIIIIIIIIIIIIIIIIIIIIZIIIIY',
'IIIIIIIIIIIIIIIIIIIIIZIIIIZ', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',
'IIIIIIIIIIIIIIIIIIIIXIXIIII', 'IIIIIIIIIIIIIIIIIIIIXIYIIII',
'IIIIIIIIIIIIIIIIIIIIXIZIIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',
'IIIIIIIIIIIIIIIIIIIIYIXIIII', 'IIIIIIIIIIIIIIIIIIIIYIYIIII',
'IIIIIIIIIIIIIIIIIIIIYIZIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',
'IIIIIIIIIIIIIIIIIIIIZIXIIII', 'IIIIIIIIIIIIIIIIIIIIZIYIIII', ...]
Along with the error rates:
[0.00037 0.00043 0.00032 ... 0.00031 0.00035 0.00017]
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 de IBM Quantum. 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='r223A', modifier_ref='', site='after')]
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 "
f"(truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
print(
f"Along with the error rates "
f"(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_5, Y_5, Z_5]>
Along with the error rates (truncated to 3):
[2.3e-04 9.0e-05 1.1e-04]
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 pasa por el gestor de pases de boxing (boxing pass manager), que agrupa las instrucciones en cajas anotadas. El código relevante se añade aquí para facilitar la comprensión.
inject_noise_targets=”gates”especifica añadir las anotacionesInjectNoisea las cajas que contienen entrelazadores.inject_noise_strategy="uniform_modification"especifica asignar el mismorefymodifier_refa todas las cajas equivalentes con anotacionesInjectNoise.InjectNoise.refes un identificador único usado 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='r223A', modifier_ref='r223A', site='after')]
Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r621A', modifier_ref='r621A', site='after')]
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)
Usar NoiseLearnerV3 con Estimator del lado del cliente
El siguiente código muestra cómo usar el programa asistente con el nuevo Estimator del lado del cliente
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
from qiskit_ibm_runtime.executor_estimator import Estimator
# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
observable = SparsePauliOp("ZZ")
# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
pubs = [(isa_circuit, isa_observable)]
# 4. Initialize Estimator with options
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True
# 5. Extract the unique boxed layers from PUBs
layers = estimator.find_unique_layers(pubs)
# 6. Learn the noise model for those layers.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()
# 7. Convert the result to Pauli-Lindblad maps and pass them to Estimator.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
estimator.options.resilience.layer_noise_model = zip(
layers, pauli_lindblad_maps
)
# 8. Execute the target PUBs.
job = estimator.run(pubs)
result = job.result()
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.
- Aprende a implementar la gestión de ruido del Estimator.