Migrar de NoiseLearner para NoiseLearnerV3
Este guia orienta você na migração do NoiseLearner do IBM Quantum® para o NoiseLearnerV3. Ambas
as classes realizam experimentos que caracterizam processos de ruído com base em um
modelo de ruído de Pauli-Lindblad, mas as entradas e
saídas são ligeiramente diferentes.
Contexto
A classe NoiseLearner
foi criada para permitir que os usuários realizem aprendizado explícito de ruído. O modelo de ruído
resultante pode então ser passado ao Estimator do IBM Quantum para aplicar técnicas de mitigação de erros,
como PEA e PEC.
NoiseLearner foi projetado para funcionar com Estimator e, portanto, emprega implicitamente a mesma
estratégia de localização de camadas que o Estimator. Essa estratégia não pode ser alterada; caso contrário, as
etapas de mitigação subsequentes não funcionariam corretamente.
A partir do qiskit-ibm-runtime v0.47.0, há uma nova
classe NoiseLearnerV3
que é compatível com a primitiva Executor e o
modelo de execução direcionada.
Esse novo modelo oferece uma experiência de caixa-branca ao fornecer as peças para capturar a intenção de design
no lado do cliente, e uma única primitiva do lado do servidor (Executor) processa essas entradas exatamente como
direcionado — ela não toma decisões implícitas em seu nome. Diferente do NoiseLearner original,
você controla como estratificar seus circuitos ao usar oNoiseLearnerV3, e a classe simplesmente recebe como entrada uma lista de instruções de circuito em caixas
(por exemplo, camadas únicas).
NoiseLearnerV3 também suporta aprendizado de ruído de medição. Para cada instrução na lista de entrada, ele executa o
protocolo de aprendizado de Pauli-Lindblad se a caixa contiver gates de um e dois qubits, e o
protocolo TREX
se a caixa contiver medições.
Você deve migrar?
NoiseLearner só funciona com o Estimator legado do lado do servidor, e NoiseLearnerV3 só funciona com
Executor e o Estimator do lado do cliente. Você deve migrar para NoiseLearnerV3 se estiver usando
Executor ou o Estimator do lado do cliente. O Estimator legado do lado do servidor está obsoleto e é substituído pelo equivalente do lado do cliente no qiskit-ibm-runtime v0.50.0.
Se você estiver usando qiskit-ibm-runtime v0.50.0 ou posterior, leia o guia Migrar do Sampler e Estimator do lado do servidor para o lado do cliente para migrar primeiro para as primitivas do lado do cliente.
Etapas de migração
Etapa 1: alterar as importações
NoiseLearner:
from qiskit_ibm_runtime.noise_learner import NoiseLearner
NoiseLearnerV3:
from qiskit_ibm_runtime import NoiseLearnerV3
Etapa 2: atualizar as entradas
O método run() do NoiseLearner recebe uma lista de circuitos ou PUBs, enquanto o método run() do NoiseLearnerV3 recebe uma lista de instruções, cada uma das quais deve ser um BoxOp anotado com twirling contendo operações ISA.
Métodos de conveniência estão disponíveis para gerar as caixas anotadas, dependendo de qual
primitiva você planeja 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, ao trabalhar com o Estimator do lado do cliente:
Se você planeja usar o Estimator do lado do cliente para execução de circuitos, pode usar o método find_unique_layers do Estimator para criar caixas anotadas (camadas):
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, ao trabalhar com o Executor:
Se você planeja usar o Executor para execução de circuitos, considere usar a função generate_boxing_pass_manager do Samplomatic para criar caixas 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)
Etapa 3: converter as opções
A maioria dos campos de NoiseLearnerOptions mapeiam diretamente para NoiseLearnerV3Options, exceto os seguintes:
-
max_layers_to_learn: comNoiseLearnerV3, o número de camadas a aprender é baseado no número de camadas passadas. -
twirling_strategy: comNoiseLearnerV3, a estratégia de twirling é definida por como as instruções são colocadas em caixas e anotadas (como ao usargenerate_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, ao trabalhar com o Estimator do lado do cliente:
Se você planeja usar o Estimator do lado do cliente para execução de circuitos, você pode
definir a opção twirling.strategy do 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, ao trabalhar com o Executor:
Se você planeja usar o Executor para execução de circuitos, pode passar a opção twirling_strategy para a função generate_boxing_pass_manager.
Observe que, com generate_boxing_pass_manager(), os valores de twirling_strategy usam sublinhados
("active_accum", "active_circuit"), enquanto os valores de NoiseLearnerOptions.twirling_strategy usam hífens ("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])
Etapa 4: inspecionar os resultados
As saídas de NoiseLearner e NoiseLearnerV3 contêm informações semelhantes, mas em formatos diferentes. Atualize seu código se ele inspecionar a saída explicitamente.
Mapeamento de atributos de resultado:
(learner_result é a saída do job de aprendizado)
| Atributo | NoiseLearner | NoiseLearnerV3 |
|---|---|---|
| Tipo de resultado | NoiseLearnerResult | NoiseLearnerV3Results, um contêiner do tipo sequência de NoiseLearnerV3Result |
| Número de camadas aprendidas | len(learner_result.data) | len(learner_result) |
| Dados da primeira camada | layer_error = learner_result.data[0] | noise_map = learner_result[0].to_pauli_lindblad_map() |
| Tipo de resultado de cada camada | LayerError (type(layer_error)) | PauliLindbladMap (type(noise_map)) |
| Geradores para o canal de erro | layer_error.error.generators | noise_map.generators() |
| Taxas de erro | layer_error.error.rates | noise_map.rates |
Etapa 5: fornecer o modelo de ruído a uma primitiva
NoiseLearner só funciona com o Estimator legado do lado do servidor, e NoiseLearnerV3 só funciona com Executor e o Estimator do lado do cliente. A forma como um modelo de ruído é especificado varia ligeiramente dependendo da primitiva usada.
NoiseLearner, ao trabalhar com o Estimator legado do lado do 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, ao trabalhar com o Estimator do lado do cliente:
Reutilize o mesmo Estimator que produziu layers na etapa 2. Os mapas de ruído retornados pelo
aprendiz são correspondidos posicionalmente a essas camadas, então devem ser atribuídos ao Estimator
do qual vieram. PEA/PEC já foi habilitado nele na etapa 2.
Observe que, embora NoiseLearnerV3 suporte os protocolos Pauli-Lindblad e TREX, Estimator só aceita modelos de ruído para camadas de dois qubits aprendidos com o 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, ao trabalhar com o 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)
Exemplos completos
NoiseLearnerV3 e Estimator do lado do 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 e 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()