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.
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: ConNoiseLearnerV3, el número de capas a aprender depende del número de capas que se le pasan como entrada. -
twirling_strategy: ConNoiseLearnerV3, la estrategia de twirling se define por cómo se empaquetan y anotan las instrucciones (por ejemplo, cuando se usagenerate_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)
| Atributo | NoiseLearner | NoiseLearnerV3 |
|---|---|---|
| Tipo de resultado | NoiseLearnerResult | NoiseLearnerV3Results, un contenedor similar a una secuencia de NoiseLearnerV3Result |
| Número de capas aprendidas | len(learner_result.data) | len(learner_result) |
| Datos de la primera capa | layer_error = learner_result.data[0] | noise_map = learner_result[0].to_pauli_lindblad_map() |
| Tipo de resultado de cada capa | LayerError (type(layer_error)) | PauliLindbladMap (type(noise_map)) |
| Generadores para el canal de error | layer_error.error.generators | noise_map.generators() |
| Tasas de error | layer_error.error.rates | noise_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()