Assistant d'apprentissage du bruit
Le code de cette page a été développé en tenant compte des exigences suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.4.1 qiskit-ibm-runtime~=0.47.0 samplomatic~=0.18.0
Les techniques d'atténuation des erreurs PEA et PEC utilisent toutes deux un composant d'apprentissage du bruit basé sur un modèle de bruit de Pauli-Lindblad, qui est généralement géré pendant l'exécution après la soumission d'une ou plusieurs tâches via qiskit-ibm-runtime [...], sans aucun accès local au modèle de bruit ajusté. Toutefois, à partir de la version qiskit-ibm-runtimev0.27.1, une classe NoiseLearner ainsi qu'une NoiseLearnerOptions classe associée ont été créées pour récupérer les résultats de ces expériences d'apprentissage du bruit. Ces résultats peuvent ensuite être enregistrés localement sous forme de NoiseLearnerResult et servir de données d'entrée pour des expériences ultérieures. Cette page présente une vue d'ensemble de son utilisation et des options associées disponibles.
De plus, à partir de la version qiskit-ibm-runtimev0.47.0, une nouvelle NoiseLearnerV3 classe compatible avec la primitive Executor a été ajoutée. Cette nouvelle version, qui s'inscrit également dans le modèle d'exécution dirigée, vous permet de spécifier explicitement les couches que vous souhaitez apprendre.
NoiseLearner fonctionne uniquement avec EstimatorV2 et NoiseLearnerV3 fonctionne uniquement avec Executor.
NoiseLearner
Aperçu
Ce NoiseLearner cours propose des expériences visant à caractériser les processus de bruit à partir d'un modèle de bruit de Pauli-Lindblad appliqué à un (ou plusieurs) circuit(s). Elle dispose d'une run() méthode qui exécute les expériences d'apprentissage et prend en entrée soit une liste de circuits, soit un objet PUB, et renvoie un objet NoiseLearnerResult contenant les canaux de bruit appris ainsi que des métadonnées sur la ou les tâches soumises. Vous trouverez ci-dessous un extrait de code illustrant l'utilisation du programme d'aide.
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()Le résultat NoiseLearnerResult.data est une liste d'objets LayerError contenant le modèle de bruit pour chaque couche d'intrication appartenant aux circuits cibles. Chacune d'entre elles stocke LayerError les informations relatives à la couche, sous la forme d'un circuit et d'un ensemble d'étiquettes de qubits, ainsi que le modèle de PauliLindbladError bruit qui a été appris pour la couche en question.
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"
)Output:
Noise learner result contains 2 entries and has the following type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>
Each element of `NoiseLearnerResult` then contains an object of type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>
And each of these `LayerError` objects possess data on the generators for the error channel:
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
'IIIIIIIIIIIIIIIIIIIIIIIIXIX', 'IIIIIIIIIIIIIIIIIIIIIIIIXIY',
'IIIIIIIIIIIIIIIIIIIIIIIIXIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYIX', 'IIIIIIIIIIIIIIIIIIIIIIIIYIY',
'IIIIIIIIIIIIIIIIIIIIIIIIYIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZIX', 'IIIIIIIIIIIIIIIIIIIIIIIIZIY',
'IIIIIIIIIIIIIIIIIIIIIIIIZIZ', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIYIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'IIIIIIIIIIIIIIIIIIIIIIXIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIII',
'IIIIIIIIIIIIIIIIIIIIIIXYIII', 'IIIIIIIIIIIIIIIIIIIIIIXZIII',
'IIIIIIIIIIIIIIIIIIIIIIYIIII', 'IIIIIIIIIIIIIIIIIIIIIIYXIII',
'IIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIYZIII',
'IIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIZXIII',
'IIIIIIIIIIIIIIIIIIIIIIZYIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIII',
'IIIIIIIIIIIIIIIIIIIIIXIIIII', 'IIIIIIIIIIIIIIIIIIIIIXXIIII',
'IIIIIIIIIIIIIIIIIIIIIXYIIII', 'IIIIIIIIIIIIIIIIIIIIIXZIIII',
'IIIIIIIIIIIIIIIIIIIIIYIIIII', 'IIIIIIIIIIIIIIIIIIIIIYXIIII',
'IIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIYZIIII',
'IIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIZXIIII',
'IIIIIIIIIIIIIIIIIIIIIZYIIII', 'IIIIIIIIIIIIIIIIIIIIIZZIIII',
'IIIIIIIIIIIIIIIIIIIIXIIIIII', 'IIIIIIIIIIIIIIIIIIIIXXIIIII',
'IIIIIIIIIIIIIIIIIIIIXYIIIII', 'IIIIIIIIIIIIIIIIIIIIXZIIIII',
'IIIIIIIIIIIIIIIIIIIIYIIIIII', 'IIIIIIIIIIIIIIIIIIIIYXIIIII',
'IIIIIIIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIIIIIIYZIIIII',
'IIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIZXIIIII',
'IIIIIIIIIIIIIIIIIIIIZYIIIII', 'IIIIIIIIIIIIIIIIIIIIZZIIIII',
'IIIIIIIIIIIIIIIIIIIXIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIII',
'IIIIIIIIIIIIIIIIIIIXYIIIIII', 'IIIIIIIIIIIIIIIIIIIXZIIIIII',
'IIIIIIIIIIIIIIIIIIIYIIIIIII', 'IIIIIIIIIIIIIIIIIIIYXIIIIII',
'IIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIYZIIIIII', ...]
Along with the error rates:
[5.9e-04 5.3e-04 5.7e-04 ... 0.0e+00 1.0e-05 0.0e+00]
L'attribut LayerError.error du résultat de l'apprentissage du bruit contient les générateurs et les taux d'erreur du modèle de Pauli Lindblad ajusté, qui se présente sous la forme suivante
où les sont les LayerError.rates et sont les opérateurs de Pauli spécifiés dans LayerError.generators.
Options d'apprentissage du bruit
Vous pouvez choisir parmi plusieurs options à fournir lors de la création d'un NoiseLearner objet. Ces options sont regroupées dans la qiskit_ibm_runtime.options.NoiseLearnerOptions classe et permettent notamment de définir le nombre maximal de couches à apprendre, le nombre de randomisations et la stratégie de rotation. Pour plus d'informations, consultez la NoiseLearnerOptions documentation de l'API.
Voici un exemple simple qui montre comment utiliser le NoiseLearnerOptions dans une NoiseLearner expérience :
# 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()Modèle de bruit d'entrée vers une primitive
Le modèle de bruit appris sur le circuit peut également servir d'entrée à la primitive EstimatorV2 « IBM Quantum ». Cette valeur peut être transmise à la primitive de plusieurs façons différentes. Les trois exemples suivants montrent comment vous pouvez transmettre directement le modèle de bruit à l'attribut estimator.options , en utilisant un ResilienceOptionsV2 objet avant d'instancier une primitive Estimator, et en lui transmettant un dictionnaire correctement formaté.
# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model# 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)# Specify options by using a dictionary
options_dict = {
"resilience_level": 2,
"resilience": {"layer_noise_model": noise_model},
}
estimator = EstimatorV2(mode=backend, options=options_dict)Une fois que le modèle de bruit a été transmis à l'objet EstimatorV2 , celui-ci peut être utilisé pour exécuter des charges de travail et mettre en œuvre des mesures d'atténuation des erreurs comme d'habitude.
NoiseLearnerV3
Aperçu
À l'instar de NoiseLearner, la NoiseLearnerV3 classe réalise des expériences visant à caractériser les processus de bruit à partir d'un modèle de bruit de Pauli-Lindblad pour un ou plusieurs circuits. Sa run() méthode prend en paramètre une liste d'instructions, dont chacune doit être une structure annotée de type « twirled » BoxOp contenant des opérations ISA.
Le résultat d'une NoiseLearnerV3 tâche contient une liste NoiseLearnerV3Result d'objets, à raison d'un pour chaque instruction d'entrée.
Le code suivant montre comment utiliser le programme d'aide.
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='r789B', modifier_ref='', site='before')]
Le résultat de la fonction est une liste NoiseLearnerV3Result d'objets, à raison d'un pour chaque ensemble d'instructions entre crochets. NoiseLearnerV3Result dispose d'une to_pauli_lindblad_map() méthode qui renvoie un PauliLindbladMap objet, lequel comporte des méthodes permettant d'extraire les générateurs, les taux d'erreur et bien plus encore.
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_0, Y_0, Z_0]>
Along with the error rates (truncated to 3):
[0.00026 0.00032 0.00023]
Options d'apprentissage du bruit
NoiseLearnerV3 prend en charge plusieurs options, notamment le nombre de randomisations et la profondeur des paires de couches, entre autres. Tout comme pour les types primitifs, vous pouvez définir les options pendant ou après l'instanciation de NoiseLearnerV3 l'objet. L'exemple de code précédent montrait comment définir les shots_per_randomization options et num_randomizations . Pour plus d'informations, consultez la NoiseLearnerV3Options documentation de l'API.
Importer un modèle de bruit dans Executor
Executor suit les intentions de conception spécifiées dans les annotations du circuit (sous la forme d'un samplex) et dans les options. InjectNoise est l'annotation permettant de préciser où injecter du bruit, et l'argument pauli_lindblad_maps samplex indique quelle carte de bruit utiliser.
Le circuit de l'exemple précédent passe par le gestionnaire de passes de boxing, qui regroupe les instructions dans des cases annotées. Le code correspondant est ajouté ici pour faciliter la compréhension.
inject_noise_targets=”gates”indique d'ajouter lesInjectNoiseannotations aux encadrés contenant des éléments d'entrelacement.inject_noise_strategy="uniform_modification"indique d'attribuer les mêmesrefetmodifier_refà toutes les cases équivalentes comportantInjectNoisedes annotations.InjectNoise.refest un identifiant unique utilisé pour attribuer un modèle de bruit à cette case.InjectNoise.modifier_refpermet de modifier l'échelle du modèle de bruit attribué à une boîte à l'aide de facteurs multiplicatifs.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)Le circuit de l'exemple précédent comporte trois cases, dont deux contiennent InjectNoise des annotations dotées d'attributs différents ref (puisqu'elles ne sont pas équivalentes).
# 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='r789B', modifier_ref='r789B', site='before')]
Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r054B', modifier_ref='r054B', site='before')]
Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]
Le résultat de la NoiseLearnerV3 tâche doit être converti en dictionnaire avant d'être transmis à Executor. Les clés de ce dictionnaire sont les InjectNoise.ref attributs, et les valeurs sont les cartes de bruit correspondantes. Ce mappage indique à Executor quels modèles de bruit injecter et à quel endroit.
Le code suivant montre comment récupérer le circuit et le NoiseLearnerV3 résultat de l'exemple précédent et les transmettre à Executor, qui générera les variantes du circuit avec les modèles de bruit injectés et les exécutera sur le matériel.
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)Etapes suivantes
- Consultez la référence de l'API EstimatorOptions et la référence de l'API ResilienceOptionsV2.
- Découvrez-en davantage sur les techniques disponibles de limitation et de suppression des erreurs.
- Découvrez comment mettre en œuvre la gestion du bruit dans Estimator.