Aiuto per l'apprendimento del rumore
Il codice di questa pagina è stato sviluppato in base ai 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 l'invio di uno o più lavori tramite qiskit-ibm-runtime senza alcun accesso locale al modello di rumore adattato. Tuttavia, a partire dalla versione qiskit-ibm-runtimev0.27.1, sono state create una classe NoiseLearner e una classe NoiseLearnerOptions associata per ottenere i risultati di questi esperimenti di apprendimento del rumore. Questi risultati possono quindi essere salvati localmente come e utilizzati come dati NoiseLearnerResult di input in esperimenti successivi. Questa pagina offre una panoramica sul suo utilizzo e sulle opzioni disponibili.
Inoltre, a partire dalla versione qiskit-ibm-runtimev0.47.0, è disponibile una nuova NoiseLearnerV3 classe compatibile con la primitiva Executor. Questa nuova versione, che rientra anch'essa nel modello di esecuzione guidata, consente di specificare in modo esplicito i livelli che si desidera addestrare.
NoiseLearner funziona solo con EstimatorV2 e e NoiseLearnerV3 funziona solo con Executor.
NoiseLearner
Panoramica
Il NoiseLearner corso prevede esperimenti volti a caratterizzare i processi di rumore sulla base di un modello di rumore di Pauli-Lindblad applicato a uno (o più) circuiti. Dispone di un run() metodo che esegue gli esperimenti di apprendimento e accetta come input un elenco di circuiti o un oggetto PUB, restituendo un oggetto NoiseLearnerResult contenente i canali di rumore appresi e i metadati relativi ai lavori inviati. Di seguito è riportato un frammento di codice che illustra come utilizzare il programma di supporto.
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 risultato NoiseLearnerResult.data è un elenco di oggetti LayerError contenenti il modello di rumore per ogni singolo strato di entanglement appartenente ai circuiti di destinazione. Ciascuno di essi memorizza LayerError le informazioni relative al livello, sotto forma di un circuito e di un insieme di etichette di qubit, insieme al modello di PauliLindbladError rumore che è stato appreso per quel determinato livello.
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'attributo LayerError.error del risultato dell'apprendimento del rumore contiene i generatori e i tassi di errore del modello Pauli Lindblad adattato, che ha la forma
dove sono gli operatori LayerError.rates e sono gli operatori di Pauli specificati in LayerError.generators.
Opzioni di apprendimento del rumore
Quando si crea un'istanza di NoiseLearner un oggetto, è possibile scegliere tra diverse opzioni. Queste opzioni sono racchiuse nella qiskit_ibm_runtime.options.NoiseLearnerOptions classe e consentono, tra l'altro, di specificare il numero massimo di livelli da addestrare, il numero di randomizzazioni e la strategia di rotazione. Per ulteriori informazioni, consultare la NoiseLearnerOptions documentazione dell'API.
Di seguito è riportato un semplice esempio che illustra come utilizzare il NoiseLearnerOptions in un NoiseLearner esperimento:
# 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()Modello di rumore in ingresso a una primitiva
Il modello di rumore appreso sul circuito può essere utilizzato anche come input per la primitiva EstimatorV2 " IBM Quantum ". Questo può essere passato alla primitiva in diversi modi. I tre esempi seguenti mostrano come sia possibile passare il modello di rumore direttamente all'attributo estimator.options , utilizzando un oggetto ResilienceOptionsV2 prima di istanziare una primitiva Estimator e passando un dizionario formattato in modo appropriato.
# 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 che il modello di rumore è stato passato all'oggetto EstimatorV2 , è possibile utilizzarlo per eseguire i carichi di lavoro e applicare le misure di mitigazione degli errori come di consueto.
NoiseLearnerV3
Panoramica
Analogamente a NoiseLearner, la NoiseLearnerV3 classe esegue esperimenti volti a caratterizzare i processi di rumore sulla base di un modello di rumore di Pauli-Lindblad per uno o più circuiti. Il suo run() metodo accetta un elenco di istruzioni, ciascuna delle quali deve essere un oggetto annotato con Twirl BoxOp contenente operazioni ISA.
Il risultato di un'operazione NoiseLearnerV3 contiene un elenco di NoiseLearnerV3Result oggetti, uno per ogni istruzione di input.
Il codice seguente mostra come utilizzare il programma di supporto.
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')]
Il risultato dell'operazione è un elenco di NoiseLearnerV3Result oggetti, uno per ogni insieme di istruzioni contenuto in una casella di input. NoiseLearnerV3Result dispone di un to_pauli_lindblad_map() metodo che restituisce un PauliLindbladMap oggetto, il quale dispone di 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"
)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]
Opzioni di apprendimento del rumore
NoiseLearnerV3 supporta diverse opzioni, tra cui il numero di randomizzazioni e la profondità delle coppie di livelli, per citarne alcune. Come per le primitive, è possibile specificare le opzioni durante o dopo l'istanziazione dell'oggetto NoiseLearnerV3 . L'esempio di codice precedente mostrava come impostare le shots_per_randomization opzioni e num_randomizations . Per ulteriori informazioni, consultare la NoiseLearnerV3Options documentazione dell'API.
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 che serve a specificare dove inserire il rumore, mentre l'argomento pauli_lindblad_maps samplex indica quale mappa di rumore utilizzare.
Il circuito dell'esempio precedente passa attraverso il gestore dei passaggi di boxing, che raggruppa le istruzioni in caselle annotate. Il codice in questione è riportato qui di seguito per facilitare la comprensione.
inject_noise_targets=”gates”specifica di aggiungere leInjectNoiseannotazioni alle caselle che contengono elementi di intreccio.inject_noise_strategy="uniform_modification"specifica di assegnare lo stessorefvaloremodifier_refa tutte le caselle equivalenti conInjectNoiseannotazioni.InjectNoise.refè un identificatore univoco utilizzato per assegnare un modello di rumore a quella casella.InjectNoise.modifier_refconsente di ridimensionare il modello di rumore assegnato a una casella 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 caselle, due delle quali contengono InjectNoise annotazioni con attributi diversi ref (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")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')]
Il risultato dell'operazione NoiseLearnerV3 deve essere convertito in un dizionario prima di essere passato a Executor. Le chiavi di questo dizionario sono gli InjectNoise.ref attributi, mentre i valori sono le mappe di rumore corrispondenti. Questa mappatura indica a Executor quali modelli di rumore inserire e in quali punti.
Il codice seguente mostra come prendere il circuito e il NoiseLearnerV3 risultato dell'esempio precedente e passarli a Executor, che genererà le varianti del circuito con i modelli di rumore inseriti 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
- Consultare il riferimento API EstimatorOptions e il riferimento API ResilienceOptionsV2.
- Scopri di più sulle tecniche disponibili per la mitigazione e la soppressione degli errori.
- Scopri come implementare la gestione del rumore in Estimator.