Przejdź do głównej treści

Pomocnik uczenia szumu

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu poniższych wymagań. Zalecamy korzystanie z tych wersji lub nowszych.

qiskit[all]~=2.4.1
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.18.0

Techniki mitygacji błędów PEA i PEC wykorzystują komponent uczenia szumu oparty na modelu szumu Pauli-Lindblad, który jest zazwyczaj zarządzany podczas wykonywania po przesłaniu jednego lub więcej zadań przez qiskit-ibm-runtime bez lokalnego dostępu do dopasowanego modelu szumu. Jednak począwszy od qiskit-ibm-runtime v0.27.1 udostępniono klasę NoiseLearner oraz powiązaną klasę NoiseLearnerOptions, które umożliwiają uzyskanie wyników tych eksperymentów uczenia szumu. Wyniki te można następnie przechowywać lokalnie jako NoiseLearnerResult i używać jako danych wejściowych w późniejszych eksperymentach. Ta strona zawiera przegląd sposobu użycia oraz dostępnych opcji.

Ponadto, począwszy od qiskit-ibm-runtime v0.47.0, dostępna jest nowa klasa NoiseLearnerV3 zgodna z prymitywem Executor. Ta nowa wersja, również będąca częścią modelu wykonania kierowanego, daje możliwość jawnego określenia warstw, które chcesz nauczyć.

uwaga

NoiseLearner działa tylko z EstimatorV2, a NoiseLearnerV3 działa tylko z Executor.

NoiseLearner

Przegląd

Klasa NoiseLearner przeprowadza eksperymenty charakteryzujące procesy szumu w oparciu o model szumu Pauli-Lindblad dla jednego lub więcej Circuit. Posiada metodę run(), która wykonuje eksperymenty uczenia i przyjmuje jako dane wejściowe listę Circuit lub PUB, a następnie zwraca NoiseLearnerResult zawierający nauczone kanały szumu oraz metadane dotyczące przesłanych zadań. Poniżej znajduje się fragment kodu demonstrujący użycie pomocnika.

# 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()

Wynikowy NoiseLearnerResult.data jest listą obiektów LayerError zawierających model szumu dla każdej pojedynczej warstwy splątującej należącej do docelowych Circuit. Każdy LayerError przechowuje informacje o warstwie w postaci Circuit i zestawu etykiet qubit, wraz z PauliLindbladError dla modelu szumu nauczonego dla danej warstwy.

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]

Atrybut LayerError.error wyniku uczenia szumu zawiera generatory i współczynniki błędów dopasowanego modelu Pauli Lindblad, który ma postać

Λ(ρ)=expjrj(PjρPjρ),\Lambda(\rho) = \exp{\sum_j r_j \left(P_j \rho P_j^\dagger - \rho\right)},

gdzie rjr_j to LayerError.rates, a PjP_j to operatory Pauliego określone w LayerError.generators.

Opcje uczenia szumu

Możesz wybrać spośród kilku opcji, które podajesz podczas tworzenia instancji obiektu NoiseLearner. Opcje te są ujęte w klasie qiskit_ibm_runtime.options.NoiseLearnerOptions i obejmują możliwość określenia maksymalnej liczby warstw do nauczenia, liczby losowań oraz strategii twirling, wśród innych. Szczegółowe informacje znajdziesz w dokumentacji API NoiseLearnerOptions.

Poniżej znajduje się prosty przykład pokazujący, jak używać NoiseLearnerOptions w eksperymencie 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()

Przekazywanie modelu szumu do prymitywu

Model szumu nauczony na obwód może być również używany jako dane wejściowe dla prymitywu EstimatorV2 zaimplementowanego w Qiskit Runtime. Można go przekazać do prymitywu na kilka różnych sposobów. Kolejne trzy przykłady pokazują, jak możesz przekazać model szumu bezpośrednio do atrybutu estimator.options, używając obiektu ResilienceOptionsV2 przed utworzeniem instancji prymitywu Estimator, oraz przez przekazanie odpowiednio sformatowanego słownika.

# 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)

Po przekazaniu modelu szumu do obiektu EstimatorV2 można go używać do uruchamiania zadań i przeprowadzania mitygacji błędów w normalny sposób.

NoiseLearnerV3

Przegląd

Podobnie jak NoiseLearner, klasa NoiseLearnerV3 przeprowadza eksperymenty charakteryzujące procesy szumu w oparciu o model szumu Pauli-Lindblad dla jednego lub więcej Circuit. Metoda run() przyjmuje listę instrukcji, z których każda musi być twirled-annotated BoxOp zawierającym operacje ISA.

Wynik zadania NoiseLearnerV3 zawiera listę obiektów NoiseLearnerV3Result, po jednym dla każdej instrukcji wejściowej. Poniższy kod pokazuje, jak używać pomocnika.

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')]

Wynik zadania to lista obiektów NoiseLearnerV3Result, po jednym dla każdego wejściowego zestawu instrukcji w boksach. NoiseLearnerV3Result posiada metodę to_pauli_lindblad_map(), która zwraca obiekt PauliLindbladMap, który ma metody do wyodrębniania generatorów, współczynników błędów i nie tylko.

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]

Opcje uczenia szumu

NoiseLearnerV3 obsługuje kilka opcji, w tym liczbę losowań i głębokość par warstw, wśród innych. Podobnie jak w przypadku prymitywów, możesz określić opcje podczas lub po utworzeniu instancji obiektu NoiseLearnerV3. Poprzedni przykład kodu demonstruje, jak ustawić opcje shots_per_randomization i num_randomizations. Szczegółowe informacje znajdziesz w dokumentacji API NoiseLearnerV3Options.

Przekazywanie modelu szumu do Executor

Executor stosuje się do intencji projektowych określonych w adnotacjach Circuit (w postaci samplex) i opcjach. InjectNoise to adnotacja służąca do określania miejsca wstrzykiwania szumu, a argument samplex pauli_lindblad_maps określa, której mapy szumu użyć.

Circuit z poprzedniego przykładu przechodzi przez boxing pass manager, który grupuje instrukcje w adnotowane boksy. Odpowiedni kod dodano tu dla ułatwienia zrozumienia.

  • inject_noise_targets="gates" określa dodanie adnotacji InjectNoise do boksów zawierających elementy splatające.
  • inject_noise_strategy="uniform_modification" określa przypisanie tego samego ref i modifier_ref do wszystkich równoważnych boksów z adnotacjami InjectNoise.
    • InjectNoise.ref to unikalny identyfikator używany do przypisania modelu szumu do danego boksu.
    • InjectNoise.modifier_ref umożliwia skalowanie modelu szumu przypisanego do boksu przez czynniki multiplikatywne.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)

Circuit z poprzedniego przykładu zawiera trzy boksy, z których dwa zawierają adnotacje InjectNoise z różnymi atrybutami ref (ponieważ nie są równoważne).

# 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')]

Wynik zadania NoiseLearnerV3 musi być przekonwertowany na słownik przed przekazaniem do Executor. Klucze tego słownika to atrybuty InjectNoise.ref, a wartości to odpowiadające im mapy szumu. To odwzorowanie informuje Executor, które modele szumu wstrzyknąć i gdzie.

Poniższy kod pokazuje, jak wziąć Circuit i wynik NoiseLearnerV3 z poprzedniego przykładu i przekazać je do Executor, który wygeneruje warianty Circuit z wstrzykniętymi modelami szumu i wykona je na sprzęcie.

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)

Następne kroki

Zalecenia