Перейти до основного вмісту

Помічник навчання шуму

Package versions

Код на цій сторінці розроблено з використанням наведених нижче вимог. Рекомендуємо використовувати ці або новіші версії.

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

Техніки пом'якшення помилок PEA та PEC обидві використовують компонент навчання шуму, що базується на моделі шуму Паулі-Ліндблада, яка зазвичай керується під час виконання після надсилання одного або кількох завдань через qiskit-ibm-runtime без локального доступу до підігнаної моделі шуму. Проте, починаючи з qiskit-ibm-runtime v0.27.1, створено класи NoiseLearner та пов'язаний NoiseLearnerOptions, щоб отримувати результати цих експериментів із навчання шуму. Ці результати можна зберігати локально як NoiseLearnerResult і використовувати як вхідні дані в подальших експериментах. Ця сторінка містить огляд його використання та доступних параметрів.

Крім того, починаючи з qiskit-ibm-runtime v0.47.0, є новий клас NoiseLearnerV3, сумісний з примітивом Executor. Ця нова версія, яка також є частиною моделі спрямованого виконання, дозволяє явно вказувати шари, які ти хочеш вивчати.

примітка

NoiseLearner працює лише з EstimatorV2, а NoiseLearnerV3 — лише з Executor.

NoiseLearner

Огляд

Клас NoiseLearner виконує експерименти, що характеризують процеси шуму на основі моделі шуму Паулі-Ліндблада для одного (або кількох) схем. Він має метод run(), який виконує навчальні експерименти і приймає на вхід або список схем, або PUB, та повертає NoiseLearnerResult, що містить вивчені канали шуму та метадані про надіслані завдання. Нижче наведено фрагмент коду, що демонструє використання помічника програми.

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

Результуючий NoiseLearnerResult.data — це список об'єктів LayerError, що містять модель шуму для кожного окремого шару заплутування, який належить до цільових схем. Кожен LayerError зберігає інформацію про шар у вигляді схеми та набору міток кубітів, поряд із PauliLindbladError для моделі шуму, яка була вивчена для відповідного шару.

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{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]

Атрибут LayerError.error результату навчання шуму містить генератори та частоти помилок підігнаної моделі Паулі-Ліндблада, яка має вигляд

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

де rjr_j — це LayerError.rates, а PjP_j — оператори Паулі, вказані в LayerError.generators.

Параметри навчання шуму

Ти можеш обрати серед кількох параметрів, які задаються під час створення екземпляра об'єкта NoiseLearner. Ці параметри інкапсульовано класом qiskit_ibm_runtime.options.NoiseLearnerOptions і включають можливість вказати максимальну кількість шарів для навчання, кількість рандомізацій та стратегію твірлінгу, серед іншого. Докладнішу інформацію дивись у документації API NoiseLearnerOptions.

Нижче наведено простий приклад, що показує, як використовувати NoiseLearnerOptions в експерименті 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()

Передача моделі шуму до примітива

Модель шуму, вивчена на схемі, також може використовуватися як вхідні дані для примітива EstimatorV2, реалізованого в Qiskit Runtime. Її можна передати до примітива кількома різними способами. Наступні три приклади показують, як передати модель шуму безпосередньо до атрибута estimator.options, використовуючи об'єкт ResilienceOptionsV2 перед створенням екземпляра примітива Estimator, та шляхом передачі відповідно відформатованого словника.

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

Після передачі моделі шуму до об'єкта EstimatorV2 його можна використовувати для запуску робочих навантажень і виконання пом'якшення помилок у звичайному режимі.

NoiseLearnerV3

Огляд

Подібно до NoiseLearner, клас NoiseLearnerV3 виконує експерименти, що характеризують процеси шуму на основі моделі шуму Паулі-Ліндблада для одної або кількох схем. Його метод run() приймає список інструкцій, кожна з яких має бути анотованим твірлінгом BoxOp, що містить операції ISA.

Результат завдання NoiseLearnerV3 містить список об'єктів NoiseLearnerV3Result, по одному для кожної вхідної інструкції. Наступний код показує, як використовувати цю програму-помічник.

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

Результат завдання — це список об'єктів NoiseLearnerV3Result, по одному для кожного вхідного набору інструкцій у боксах. NoiseLearnerV3Result має метод to_pauli_lindblad_map(), який повертає об'єкт PauliLindbladMap, що має методи для вилучення генераторів, частот помилок тощо.

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 (truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
print(
f"Along with the error rates (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]

Параметри навчання шуму

NoiseLearnerV3 підтримує кілька параметрів, включаючи кількість рандомізацій та глибину пар шарів, серед іншого. Подібно до примітивів, ти можеш задавати параметри під час або після створення екземпляра об'єкта NoiseLearnerV3. Попередній приклад коду демонструє, як встановити параметри shots_per_randomization та num_randomizations. Докладнішу інформацію дивись у документації API NoiseLearnerV3Options.

Передача моделі шуму до Executor

Executor дотримується намірів дизайну, заданих в анотаціях схеми (у формі семплексу) та параметрах. InjectNoise — це анотація для вказання місця введення шуму, а аргумент семплексу pauli_lindblad_maps визначає, яку карту шуму використовувати.

Схема з попереднього прикладу проходить через менеджер пасів боксування, який групує інструкції в анотовані бокси. Відповідний код наведено тут для зручності розуміння.

  • inject_noise_targets="gates" вказує додавати анотації InjectNoise до боксів, що містять заплутувачі.
  • inject_noise_strategy="uniform_modification" вказує призначати однаковий ref та modifier_ref усім еквівалентним боксам з анотаціями InjectNoise.
    • InjectNoise.ref — унікальний ідентифікатор, що використовується для присвоєння моделі шуму тому боксу.
    • InjectNoise.modifier_ref дозволяє масштабувати модель шуму, призначену боксу, за допомогою мультиплікативних коефіцієнтів.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)

Схема з попереднього прикладу містить три бокси, два з яких мають анотації InjectNoise з різними атрибутами ref (оскільки вони не є еквівалентними).

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

Результат завдання NoiseLearnerV3 потрібно перетворити на словник перед передачею до Executor. Ключами цього словника є атрибути InjectNoise.ref, а значеннями — відповідні карти шуму. Це відображення повідомляє Executor, які моделі шуму вводити і де.

Наступний код показує, як взяти схему та результат NoiseLearnerV3 з попереднього прикладу і передати їх до Executor, який генерує варіанти схеми з введеними моделями шуму та виконує їх на апаратному забезпеченні.

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)

Наступні кроки

Рекомендації