Helper per l'apprendimento del rumore
Versioni dei pacchetti
Il codice in questa pagina è stato sviluppato usando i seguenti requisiti. Si consiglia di utilizzare queste versioni o versioni più recenti.
qiskit[all]~=2.4.1
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.18.0
Le tecniche di mitigazione degli errori PEA e PEC utilizzano entrambe una componente di apprendimento del rumore basata su un modello di rumore di Pauli-Lindblad, che viene tipicamente gestita durante l'esecuzione dopo aver inviato uno o più job tramite qiskit-ibm-runtime, senza alcun accesso locale al modello di rumore addestrato. Tuttavia, a partire da qiskit-ibm-runtime v0.27.1, sono state create le classi NoiseLearner e NoiseLearnerOptions per ottenere i risultati di questi esperimenti di apprendimento del rumore. I risultati possono poi essere salvati localmente come NoiseLearnerResult e usati come input in esperimenti successivi. Questa pagina fornisce una panoramica del loro utilizzo e delle opzioni disponibili.
Inoltre, a partire da qiskit-ibm-runtime v0.47.0, è disponibile una nuova classe NoiseLearnerV3 compatibile con la primitiva Executor. Questa nuova versione, anch'essa parte del modello di esecuzione diretta, consente di specificare esplicitamente i layer che si desidera apprendere.
NoiseLearner funziona solo con EstimatorV2 e NoiseLearnerV3 funziona solo con Executor.
NoiseLearner
Panoramica
La classe NoiseLearner esegue esperimenti che caratterizzano i processi di rumore basandosi su un modello di rumore di Pauli-Lindblad per uno o più circuiti. Dispone di un metodo run() che esegue gli esperimenti di apprendimento e accetta in input una lista di circuiti oppure un PUB, restituendo un NoiseLearnerResult contenente i canali di rumore appresi e i metadati relativi ai job inviati. Di seguito è riportato uno snippet di codice che illustra l'utilizzo del programma helper.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
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()
Il risultante NoiseLearnerResult.data è una lista di oggetti LayerError contenente il modello di rumore per ogni singolo layer di entanglement appartenente ai circuiti target. Ogni LayerError memorizza le informazioni del layer, sotto forma di circuito e insieme di etichette di qubit, insieme al PauliLindbladError per il modello di rumore appreso per il layer in questione.
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"
)
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'attributo LayerError.error del risultato dell'apprendimento del rumore contiene i generatori e i tassi di errore del modello di Pauli-Lindblad adattato, che ha la forma
dove gli sono i LayerError.rates e i sono gli operatori di Pauli specificati in LayerError.generators.
Opzioni per l'apprendimento del rumore
Puoi scegliere tra diverse opzioni da specificare quando istanzi un oggetto NoiseLearner. Queste opzioni sono racchiuse nella classe qiskit_ibm_runtime.options.NoiseLearnerOptions e includono la possibilità di specificare il numero massimo di layer da apprendere, il numero di randomizzazioni e la strategia di twirling, tra le altre. Per informazioni più dettagliate, consulta la documentazione API su NoiseLearnerOptions.
Di seguito è riportato un semplice esempio che mostra come usare NoiseLearnerOptions in un esperimento con NoiseLearner:
# 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()
Inserire il modello di rumore in una primitiva
Il modello di rumore appreso sul circuito può essere usato anche come input per la primitiva EstimatorV2 implementata in Qiskit Runtime. Può essere passato alla primitiva in diversi modi. I tre esempi successivi mostrano come puoi passare il modello di rumore all'attributo estimator.options direttamente, tramite un oggetto ResilienceOptionsV2 prima di istanziare una primitiva Estimator, oppure passando un dizionario nel formato corretto.
# 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)
Una volta passato il modello di rumore all'oggetto EstimatorV2, questo può essere usato per eseguire workload ed effettuare la mitigazione degli errori normalmente.
NoiseLearnerV3
Panoramica
Analogamente a NoiseLearner, la classe NoiseLearnerV3 esegue esperimenti che caratterizzano i processi di rumore basandosi su un modello di rumore di Pauli-Lindblad per uno o più circuiti. Il suo metodo run() accetta una lista di istruzioni, ognuna delle quali deve essere un BoxOp annotato con twirling che contenga operazioni ISA.
Il risultato di un job NoiseLearnerV3 contiene una lista di oggetti NoiseLearnerV3Result, uno per ciascuna istruzione in input.
Il seguente codice mostra come utilizzare il programma helper.
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()
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')]
Il risultato del job è una lista di oggetti NoiseLearnerV3Result, uno per ogni insieme di istruzioni in box di input. NoiseLearnerV3Result dispone di un metodo to_pauli_lindblad_map() che restituisce un oggetto PauliLindbladMap, che fornisce metodi per estrarre i generatori, i tassi di errore e altro ancora.
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"
)
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]
Opzioni per l'apprendimento del rumore
NoiseLearnerV3 supporta diverse opzioni, tra cui il numero di randomizzazioni e la profondità delle coppie di layer, tra le altre. Analogamente alle primitive, è possibile specificare le opzioni durante o dopo l'istanziazione dell'oggetto NoiseLearnerV3. L'esempio di codice precedente ha illustrato come impostare le opzioni shots_per_randomization e num_randomizations. Per informazioni più dettagliate, consulta la documentazione API su NoiseLearnerV3Options.
Inserire un modello di rumore in Executor
Executor segue le intenzioni di progettazione specificate nelle annotazioni del circuito (sotto forma di samplex) e nelle opzioni. InjectNoise è l'annotazione per specificare dove iniettare il rumore, e l'argomento samplex pauli_lindblad_maps specifica quale mappa di rumore utilizzare.
Il circuito nell'esempio precedente viene elaborato attraverso il boxing pass manager, che raggruppa le istruzioni in box annotati. Il codice rilevante è riportato qui per facilità di comprensione.
inject_noise_targets="gates"specifica di aggiungere le annotazioniInjectNoiseai box che contengono gate di entanglement.inject_noise_strategy="uniform_modification"specifica di assegnare lo stessorefemodifier_refa tutti i box equivalenti con annotazioniInjectNoise.InjectNoise.refè un identificatore univoco usato per assegnare un modello di rumore a quel box.InjectNoise.modifier_refconsente di scalare il modello di rumore assegnato a un box mediante fattori moltiplicativi.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
Il circuito dell'esempio precedente contiene tre box, due dei quali contengono annotazioni InjectNoise con attributi ref diversi (poiché non sono equivalenti).
# 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")
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')]
Il risultato del job NoiseLearnerV3 deve essere convertito in un dizionario prima di essere passato a Executor. Le chiavi di questo dizionario sono gli attributi InjectNoise.ref e i valori sono le mappe di rumore corrispondenti. Questa mappatura indica a Executor quali modelli di rumore iniettare e dove.
Il seguente codice mostra come prendere il circuito e il risultato NoiseLearnerV3 dall'esempio precedente e passarli a Executor, che genererà le varianti del circuito con i modelli di rumore iniettati e le eseguirà sull'hardware.
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)
Passi successivi
- Consulta il riferimento API di EstimatorOptions e il riferimento API di ResilienceOptionsV2.
- Scopri di più sulle tecniche di mitigazione e soppressione degli errori disponibili tramite Qiskit Runtime.
- Scopri come implementare la gestione del rumore con Estimator.
- Leggi Migra alle primitive V2.