Migrer de NoiseLearner vers NoiseLearnerV3
Ce guide t'accompagne dans la migration de NoiseLearner vers NoiseLearnerV3 d'IBM Quantum®. Les deux
classes effectuent des expériences qui caractérisent les processus de bruit sur la base d'un
modèle de bruit de Pauli-Lindblad, mais les entrées et
les sorties sont légèrement différentes.
Contexte
La classe NoiseLearner
a été créée pour permettre aux utilisateurs d'effectuer un apprentissage explicite du bruit. Le modèle de bruit
résultant peut ensuite être passé à Estimator d'IBM Quantum pour appliquer des techniques de mitigation d'erreurs
telles que PEA et PEC.
NoiseLearner a été conçu pour fonctionner avec Estimator, et il utilise donc implicitement la même
stratégie de recherche de couches qu'Estimator. Cette stratégie ne peut pas être modifiée ; sinon, les étapes
de mitigation suivantes ne fonctionneraient pas correctement.
À partir de qiskit-ibm-runtime v0.47.0, il existe une nouvelle
classe NoiseLearnerV3
compatible avec la primitive Executor et le
modèle d'exécution dirigée.
Ce nouveau modèle offre une expérience en boîte blanche en fournissant les éléments permettant de capturer l'intention de conception
côté client, et une seule primitive côté serveur (Executor) traite ces entrées exactement comme
indiqué — elle ne prend aucune décision implicite en ton nom. Contrairement à l'original NoiseLearner,
tu contrôles la façon de stratifier tes circuits lorsque tu utilisesNoiseLearnerV3, et la classe prend simplement en entrée une liste d'instructions de circuit encadrées
(par exemple, des couches uniques).
NoiseLearnerV3 prend également en charge l'apprentissage du bruit de mesure. Pour chaque instruction de la liste d'entrée, il exécute le
protocole d'apprentissage de Pauli-Lindblad si l'encadré contient des portes à un et deux qubits, et le
protocole TREX
si l'encadré contient des mesures.
Dois-tu migrer ?
NoiseLearner fonctionne uniquement avec l'ancien Estimator côté serveur, et NoiseLearnerV3 fonctionne uniquement avec
Executor et l'Estimator côté client. Tu dois migrer vers NoiseLearnerV3 si tu utilises
Executor ou l'Estimator côté client. L'ancien Estimator côté serveur est obsolète et remplacé par l'équivalent côté client dans qiskit-ibm-runtime v0.50.0.
Si tu utilises qiskit-ibm-runtime v0.50.0 ou une version ultérieure, lis d'abord le guide Migrer de Sampler et Estimator côté serveur vers côté client pour migrer vers les primitives côté client.
Étapes de migration
Étape 1 : Changer les imports
NoiseLearner:
from qiskit_ibm_runtime.noise_learner import NoiseLearner
NoiseLearnerV3:
from qiskit_ibm_runtime import NoiseLearnerV3
Étape 2 : Mettre à jour les entrées
La méthode run() de NoiseLearner prend une liste de circuits ou de PUB, tandis que la méthode run() de NoiseLearnerV3 prend une liste d'instructions, chacune devant être un BoxOp annoté pour le twirling contenant des opérations ISA.
Des méthodes de commodité sont disponibles pour générer les encadrés annotés, selon la
primitive que tu prévois d'utiliser.
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, en travaillant avec Estimator côté client :
Si tu prévois d'utiliser Estimator côté client pour l'exécution des circuits, tu peux utiliser la méthode find_unique_layers d'Estimator pour créer des encadrés annotés (couches) :
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, en travaillant avec Executor :
Si tu prévois d'utiliser Executor pour l'exécution des circuits, envisage d'utiliser la fonction generate_boxing_pass_manager de Samplomatic pour créer des encadrés annotés :
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)
Étape 3 : Convertir les options
La plupart des champs de NoiseLearnerOptions sont directement mappés vers NoiseLearnerV3Options, à l'exception des suivants :
-
max_layers_to_learn: AvecNoiseLearnerV3, le nombre de couches à apprendre est basé sur le nombre de couches passées en entrée. -
twirling_strategy: AvecNoiseLearnerV3, la stratégie de twirling est définie par la façon dont les instructions sont encadrées et annotées (par exemple lors de l'utilisation degenerate_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, en travaillant avec Estimator côté client :
Si tu prévois d'utiliser Estimator côté client pour l'exécution des circuits, tu peux
définir l'option twirling.strategy d'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, en travaillant avec Executor :
Si tu prévois d'utiliser Executor pour l'exécution des circuits, tu peux passer l'option twirling_strategy à la fonction generate_boxing_pass_manager.
Note qu'avec generate_boxing_pass_manager(), les valeurs de twirling_strategy utilisent des underscores
("active_accum", "active_circuit"), tandis que les valeurs de NoiseLearnerOptions.twirling_strategy utilisent des tirets ("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])
Étape 4 : Inspecter les résultats
Les sorties de NoiseLearner et NoiseLearnerV3 contiennent des informations similaires mais dans des formats différents. Met à jour ton code s'il inspecte explicitement la sortie.
Mappage des attributs de résultat :
(learner_result est la sortie du job de learner)
| Attribut | NoiseLearner | NoiseLearnerV3 |
|---|---|---|
| Type de résultat | NoiseLearnerResult | NoiseLearnerV3Results, un conteneur de type séquence de NoiseLearnerV3Result |
| Nombre de couches apprises | len(learner_result.data) | len(learner_result) |
| Données pour la première couche | layer_error = learner_result.data[0] | noise_map = learner_result[0].to_pauli_lindblad_map() |
| Type de résultat de chaque couche | LayerError (type(layer_error)) | PauliLindbladMap (type(noise_map)) |
| Générateurs pour le canal d'erreur | layer_error.error.generators | noise_map.generators() |
| Taux d'erreur | layer_error.error.rates | noise_map.rates |
Étape 5 : Fournir le modèle de bruit à une primitive
NoiseLearner fonctionne uniquement avec l'ancien Estimator côté serveur, et NoiseLearnerV3 fonctionne uniquement avec Executor et l'Estimator côté client. La façon dont un modèle de bruit est spécifié varie légèrement selon la primitive utilisée.
NoiseLearner, en travaillant avec l'ancien Estimator côté serveur :
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, en travaillant avec Estimator côté client :
Réutilise le même Estimator qui a produit layers à l'étape 2. Les cartes de bruit renvoyées par le
learner sont associées positionnellement à ces couches, elles doivent donc être assignées à l'Estimator
dont elles proviennent. PEA/PEC était déjà activé sur celui-ci à l'étape 2.
Note que bien que NoiseLearnerV3 prenne en charge les protocoles Pauli-Lindblad et TREX, Estimator n'accepte que les modèles de bruit pour les couches à deux qubits appris avec le protocole 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, en travaillant avec 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)
Exemples complets
NoiseLearnerV3 et Estimator côté client
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 et 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()