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.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 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 el « 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 ).


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 un NoiseLearnerV3 trabajo contiene una lista de objetos NoiseLearnerV3Result , 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='r223A', modifier_ref='', site='after')]

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.

import numpy

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_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 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='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 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)

Uso de « NoiseLearnerV3 » con el Estimator del lado del cliente

El siguiente código muestra cómo utilizar el programa auxiliar 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()

NoiseLearner

A partir de la v0.51.0, qiskit_ibm_runtime NoiseLearner ha quedado obsoleto. Consulta la guía «Migrar a NoiseLearnerV3 » para obtener instrucciones sobre cómo realizar la migración.

  • Visión general

    En esta clase NoiseLearner se realizan experimentos que caracterizan los procesos de ruido basándose en un modelo de ruido de Pauli-Lindblad para uno (o más) circuitos. Cuenta con un método run() que ejecuta los experimentos de aprendizaje y toma como entrada una lista de circuitos o un PUB, y devuelve un objeto que contiene NoiseLearnerResult los canales de ruido aprendidos y los metadatos sobre los trabajos enviados. A continuación se muestra un fragmento de código que ilustra 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 objetos LayerError que contienen el modelo de ruido para cada capa de entrelazamiento individual que pertenece a los circuitos de destino. Cada una almacena LayerError la información de la capa, en forma de circuito y de un conjunto de etiquetas de qubits, junto PauliLindbladError con el 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"
        )

    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

    Λ(ρ)=exp⁡∑jrj(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 que introducir al instanciar un objeto NoiseLearner . Estas opciones están encapsuladas en la clase qiskit_ibm_runtime.options.NoiseLearnerOptions 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 «twirling», entre otras. Consulta la documentación de la API NoiseLearnerOptions 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 primitiva EstimatorV2 « IBM Quantum ». 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.

    # Option 1: Pass the noise model to the `estimator.options` attribute directly
    estimator = EstimatorV2(mode=backend)
    estimator.options.resilience.layer_noise_model = noise_model
    
    # Option 2: 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)
    
    # Option 3: 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.


Próximos pasos

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