Skip to main content
IBM Quantum Platform

Ayudante para aprender sobre el ruido

  • El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o versiones más recientes.

    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 ambas un componente de aprendizaje del ruido basado en un modelo de ruido de Pauli-Lindblad, que normalmente se gestiona durante la ejecución tras enviar uno o varios trabajos a través de qiskit-ibm-runtime sin ningún tipo de acceso local al modelo de ruido ajustado. Sin embargo, en qiskit-ibm-runtimev0.27.1 se han creado una NoiseLearner clase y otra NoiseLearnerOptions asociada para obtener los resultados de estos experimentos de aprendizaje con ruido. Estos resultados pueden almacenarse localmente como un NoiseLearnerResult y utilizarse como datos de entrada en experimentos posteriores. Esta página ofrece una descripción general de su uso y de las opciones disponibles.

Además, a partir de la versión qiskit-ibm-runtimev0.47.0, hay una nueva NoiseLearnerV3 clase que es compatible con el tipo primitivo Executor. Esta nueva versión, que también forma parte del modelo de ejecución dirigida, te permite especificar explícitamente las capas que deseas aprender.

Note

NoiseLearner solo funciona con EstimatorV2 y NoiseLearnerV3 solo funciona con Executor.


NoiseLearner

Visión general

En esta NoiseLearner clase se realizan experimentos para caracterizar los procesos de ruido basándose en un modelo de ruido de Pauli-Lindblad para uno (o más) circuitos. Dispone de un run() método que ejecuta los experimentos de aprendizaje y toma como entrada una lista de circuitos o un objeto PUB, y devuelve un objeto NoiseLearnerResult que contiene los canales de ruido aprendidos y los metadatos sobre los trabajos enviados. A continuación se muestra un fragmento de código que muestra cómo se utiliza el programa auxiliar.

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 resultado NoiseLearnerResult.data es una lista de LayerError objetos que contienen el modelo de ruido para cada capa de entrelazamiento individual que pertenece al circuito o circuitos de destino. Cada una LayerError almacena la información de la capa, en forma de circuito y de un conjunto de etiquetas de qubits, junto con el PauliLindbladError modelo de ruido que se ha aprendido para esa capa concreta.

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"
    )

Output:

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 del ruido contiene los generadores y las tasas de error del modelo Pauli Lindblad ajustado, que tiene la forma

Λ(ρ)=expjrj(PjρPjρ),\Lambda(\rho) = \exp{\sum_j r_j \left(P_j \rho P_j^\dagger - \rho\right)},

donde rjr_j son los LayerError.rates y PjP_j son los operadores de Pauli especificados en LayerError.generators.

Opciones de aprendizaje sobre ruido

Puedes elegir entre varias opciones a la hora de crear una instancia NoiseLearner de un objeto. Estas opciones están integradas en la qiskit_ibm_runtime.options.NoiseLearnerOptions clase e incluyen la posibilidad de especificar el número máximo de capas que se van a aprender, el número de aleatorizaciones y la estrategia de rotación, entre otras. Consulte la documentación de la NoiseLearnerOptions API para obtener información detallada.

A continuación se muestra un ejemplo sencillo que ilustra cómo utilizar el NoiseLearnerOptions en un NoiseLearner experimento:

# 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()

Introducir modelo de ruido en una primitiva

El modelo de ruido aprendido en el circuito también puede utilizarse como entrada para la EstimatorV2 primitiva implementada en Qiskit Runtime Esto se puede pasar a la primitiva de varias formas diferentes. Los tres ejemplos siguientes muestran cómo se puede pasar el modelo de ruido directamente al estimator.options atributo, utilizando un ResilienceOptionsV2 objeto 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)

Una vez que el modelo de ruido se ha pasado al EstimatorV2 objeto, este puede utilizarse para ejecutar cargas de trabajo y llevar a cabo la mitigación de errores con total normalidad.


NoiseLearnerV3

Visión general

Al igual que NoiseLearner, la NoiseLearnerV3 clase lleva a cabo experimentos que caracterizan los procesos de ruido basándose en un modelo de ruido de Pauli-Lindblad para uno o varios circuitos. Su run() método toma una lista de instrucciones, cada una de las cuales debe ser una estructura «twirled» anotada BoxOp que contenga operaciones ISA.

El resultado de una NoiseLearnerV3 tarea contiene una lista de NoiseLearnerV3Result objetos, uno por cada instrucción de entrada. El siguiente código muestra cómo utilizar el programa auxiliar.

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()

Output:

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 de la operación es una lista de NoiseLearnerV3Result objetos, uno por cada conjunto de instrucciones entre corchetes. NoiseLearnerV3Result tiene un to_pauli_lindblad_map() método que devuelve un PauliLindbladMap objeto, el cual cuenta con métodos para extraer los generadores, las tasas de error y mucho 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"
    )

Output:

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 sobre ruido

NoiseLearnerV3 ofrece varias opciones, entre las que se incluyen el número de aleatorizaciones y la profundidad de los pares de capas, entre otras. Al igual que con los tipos primitivos, puedes especificar las opciones durante o después de instanciar el NoiseLearnerV3 objeto. El ejemplo de código anterior mostraba cómo configurar las opciones shots_per_randomization y num_randomizations . Consulte la documentación de la NoiseLearnerV3Options API para obtener información detallada.

Introducir un modelo de ruido en Executor

Executor sigue las intenciones de diseño especificadas en las anotaciones del circuito (en forma de samplex) y en las opciones. InjectNoise es la anotación que especifica dónde inyectar el ruido, y el pauli_lindblad_maps argumento samplex indica qué mapa de ruido se debe utilizar.

El circuito del ejemplo anterior pasa por el gestor de pasos de encajado, que agrupa las instrucciones en bloques anotados. Se incluye aquí el código correspondiente para facilitar la comprensión.

  • inject_noise_targets=”gates” especifica que se añadan las InjectNoise anotaciones a los recuadros que contienen elementos de entrelazamiento.
  • inject_noise_strategy="uniform_modification" especifica que se asignen los mismos ref y modifier_ref a todas las casillas equivalentes con InjectNoise anotaciones.
    • InjectNoise.ref es un identificador único que se utiliza para asignar un modelo de ruido a esa casilla.
    • InjectNoise.modifier_ref permite ajustar el modelo de ruido asignado a un cuadro 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 recuadros, dos de los cuales contienen InjectNoise anotaciones con atributos diferentes ref (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")

Output:

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 NoiseLearnerV3 trabajo debe convertirse en un diccionario antes de pasárselo a Executor. Las claves de este diccionario son los InjectNoise.ref atributos y los valores son los mapas de ruido correspondientes. Esta asignación indica a Executor qué modelos de ruido debe insertar y en qué lugar.

El siguiente código muestra cómo tomar el circuito y el NoiseLearnerV3 resultado del ejemplo anterior y pasarlos a Executor, que generará las variantes del circuito con los modelos de ruido inyectados y las ejecutará en el 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

Recomendaciones
¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.