Saltar al contenido principal

Migrar de NoiseLearner a NoiseLearnerV3

Esta guía te muestra cómo migrar de IBM Quantum® NoiseLearner a NoiseLearnerV3. Ambas clases realizan experimentos que caracterizan los procesos de ruido basados en un modelo de ruido de Pauli-Lindblad, pero las entradas y salidas son ligeramente diferentes.

Contexto​

La clase NoiseLearner se creó para permitir a los usuarios realizar aprendizaje de ruido explícito. El modelo de ruido resultante se puede pasar luego a Estimator de IBM Quantum para aplicar técnicas de mitigación de errores como PEA y PEC.

NoiseLearner fue diseñado para funcionar con Estimator, y por lo tanto emplea implícitamente la misma estrategia de búsqueda de capas que Estimator. Esta estrategia no se puede cambiar; de lo contrario, los pasos de mitigación posteriores no funcionarían correctamente.

A partir de qiskit-ibm-runtime v0.47.0, existe una nueva clase NoiseLearnerV3 que es compatible con la primitiva Executor y el modelo de ejecución dirigida. Este nuevo modelo ofrece una experiencia de caja blanca al proporcionar las piezas para capturar la intención de diseño en el lado del cliente, y una única primitiva del lado del servidor (Executor) procesa esas entradas exactamente como se indica — no toma decisiones implícitas en tu nombre. A diferencia del NoiseLearner original, tú controlas cómo estratificar tus circuitos cuando usas NoiseLearnerV3, y la clase simplemente toma como entrada una lista de instrucciones de circuito en cajas (por ejemplo, capas distintas).

NoiseLearnerV3 también admite el aprendizaje de ruido de medición. Para cada instrucción en la lista de entrada, ejecuta el protocolo de aprendizaje de Pauli-Lindblad si la caja contiene gates de uno y dos qubits, y el protocolo TREX si la caja contiene mediciones.

¿Debes migrar?​

NoiseLearner solo funciona con el Estimator heredado del lado del servidor, y NoiseLearnerV3 solo funciona con Executor y el Estimator del lado del cliente. Debes migrar a NoiseLearnerV3 si estás usando Executor o el Estimator del lado del cliente. El Estimator heredado del lado del servidor está obsoleto y se reemplaza por el equivalente del lado del cliente en qiskit-ibm-runtime v0.50.0.

nota

Si estás usando qiskit-ibm-runtime v0.50.0 o posterior, lee primero la guía Migrar de Sampler y Estimator del lado del servidor a los del lado del cliente para migrar a las primitivas del lado del cliente.

Pasos de migración​

Paso 1: Cambiar las importaciones​

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

NoiseLearnerV3:

from qiskit_ibm_runtime import NoiseLearnerV3

Paso 2: Actualizar las entradas​

El método run() de NoiseLearner toma una lista de circuitos o PUB, mientras que el método run() de NoiseLearnerV3 toma una lista de instrucciones, cada una de las cuales debe ser un BoxOp anotado con twirling que contenga operaciones ISA. Hay métodos de conveniencia disponibles para generar las cajas anotadas, según la primitiva que planees usar.

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

learner = NoiseLearner(mode=backend)
# `circuits_to_learn` is a list of ISA QuantumCircuit
learner_job = learner.run(circuits_to_learn)

NoiseLearnerV3, cuando se trabaja con Estimator del lado del cliente:

Si planeas usar Estimator del lado del cliente para la ejecución de circuitos, puedes usar el método find_unique_layers de Estimator para crear cajas anotadas (capas):

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)

NoiseLearnerV3, cuando se trabaja con Executor:

Si planeas usar Executor para la ejecución de circuitos, considera usar la función generate_boxing_pass_manager de Samplomatic para crear cajas anotadas:

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

# Run the boxing pass manager to group instructions into annotated boxes.
# `isa_circuit` is an ISA QuantumCircuit.
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)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)

Paso 3: Convertir las opciones​

La mayoría de los campos de NoiseLearnerOptions se corresponden directamente con NoiseLearnerV3Options, excepto los siguientes:

  • max_layers_to_learn: Con NoiseLearnerV3, el número de capas a aprender depende del número de capas que se le pasan como entrada.

  • twirling_strategy: Con NoiseLearnerV3, la estrategia de twirling se define por cómo se empaquetan y anotan las instrucciones (por ejemplo, cuando se usa generate_boxing_pass_manager()).

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import NoiseLearnerOptions

# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)

learner = NoiseLearner(mode=backend, options=learner_options)
learner_job = learner.run(circuits_to_learn)

NoiseLearnerV3, cuando se trabaja con Estimator del lado del cliente:

Si planeas usar Estimator del lado del cliente para la ejecución de circuitos, puedes establecer la opción twirling.strategy de Estimator:

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options

pubs = [...] # Your PUBs
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.twirling.strategy = "all" # set twirling strategy here

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner_options = NoiseLearnerV3Options(num_randomizations=32)
learner = NoiseLearnerV3(backend, options=learner_options)

# Learn just the first 3 layers.
learner_job = learner.run(layers[:3])

NoiseLearnerV3, cuando se trabaja con Executor:

Si planeas usar Executor para la ejecución de circuitos, puedes pasar la opción twirling_strategy a la función generate_boxing_pass_manager.

Ten en cuenta que con generate_boxing_pass_manager(), los valores de twirling_strategy usan guiones bajos ("active_accum", "active_circuit"), mientras que los valores de NoiseLearnerOptions.twirling_strategy usan guiones ("active-accum", "active-circuit").

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# Run the boxing pass manager to group instructions into annotated boxes
# `isa_circuit` is an ISA QuantumCircuit
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
twirling_strategy="all", # twirling strategy can be specified here
inject_noise_targets="gates",
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)

learner_options = NoiseLearnerV3Options(num_randomizations=32)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend, options=learner_options)
# Learn just the first 3 layers.
learner_job = learner.run(unique_box_instructions[:3])

Paso 4: Inspeccionar los resultados​

Las salidas de NoiseLearner y NoiseLearnerV3 contienen información similar pero tienen formatos diferentes. Actualiza tu código si inspecciona la salida de forma explícita.

Correspondencia de atributos del resultado:

(learner_result es la salida del job del learner)

AtributoNoiseLearnerNoiseLearnerV3
Tipo de resultadoNoiseLearnerResultNoiseLearnerV3Results, un contenedor similar a una secuencia de NoiseLearnerV3Result
Número de capas aprendidaslen(learner_result.data)len(learner_result)
Datos de la primera capalayer_error = learner_result.data[0]noise_map = learner_result[0].to_pauli_lindblad_map()
Tipo de resultado de cada capaLayerError (type(layer_error))PauliLindbladMap (type(noise_map))
Generadores para el canal de errorlayer_error.error.generatorsnoise_map.generators()
Tasas de errorlayer_error.error.ratesnoise_map.rates

Paso 5: Proporcionar el modelo de ruido a una primitiva​

NoiseLearner solo funciona con el Estimator heredado del lado del servidor, y NoiseLearnerV3 solo funciona con Executor y el Estimator del lado del cliente. La forma en que se especifica un modelo de ruido varía ligeramente según la primitiva utilizada.

NoiseLearner, cuando se trabaja con el Estimator heredado del lado del servidor:

from qiskit_ibm_runtime import Estimator as LegacyEstimator

learner_result = learner_job.result()

# Pass the noise model to the `estimator.options` attribute directly
estimator = LegacyEstimator(mode=backend)
estimator.options.resilience.layer_noise_model = learner_result
job = estimator.run(pubs)

NoiseLearnerV3, cuando se trabaja con Estimator del lado del cliente:

Reutiliza el mismo Estimator que produjo layers en el paso 2. Los mapas de ruido devueltos por el learner se corresponden posicionalmente con esas capas, por lo que deben asignarse al Estimator del que provienen. PEA/PEC ya estaba habilitado en él en el paso 2.

Ten en cuenta que aunque NoiseLearnerV3 admite tanto el protocolo Pauli-Lindblad como TREX, Estimator solo acepta modelos de ruido para capas de dos qubits aprendidos con el protocolo Pauli-Lindblad.

learner_result = learner_job.result()

# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# Now execute the target PUBs.
job = estimator.run(pubs)

NoiseLearnerV3, cuando se trabaja con Executor:

from qiskit_ibm_runtime import Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Convert the NoiseLearnerV3 result to a dictionary
learner_result = learner_job.result()
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)

Ejemplos completos​

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

NoiseLearnerV3 y Executor​

from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor, NoiseLearnerV3
from qiskit_ibm_runtime.quantum_program import QuantumProgram

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# 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)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# 4. 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)

# 5. Find unique boxed instructions (layers)
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

# 6. Learn the noise model for those layers
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()

# 7. Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# 8. Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# 9. Prepare a quantum program
program = QuantumProgram(shots=1000)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
job = executor.run(program)
result = job.result()

Referencias​