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

Квантовий шум і пом'якшення помилок

примітка

Toshinari Itoko (28 червня 2024)

Завантажити PDF оригінальної лекції. Зверни увагу, що деякі фрагменти коду можуть бути застарілими, оскільки це статичні зображення.

Приблизний час QPU для виконання цього експерименту — 1 хв 40 с.

1. Вступ

У цьому уроці ми розглянемо шум і способи його пом'якшення на квантових комп'ютерах. Спочатку ми подивимося на ефекти шуму за допомогою симулятора, який може моделювати шум кількома способами, зокрема використовуючи шумові профілі реальних квантових комп'ютерів. Потім перейдемо до реальних квантових комп'ютерів, де шум є невід'ємною частиною. Ми розглянемо ефекти пом'якшення помилок, включно з комбінаціями методів, таких як екстраполяція нульового шуму (ZNE) і gate-twirling.

Почнемо з завантаження пакетів.

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib qiskit qiskit-aer qiskit-ibm-runtime
# !pip install qiskit qiskit_aer qiskit_ibm_runtime
# !pip install jupyter
# !pip install matplotlib pylatexenc
import qiskit

qiskit.__version__
'2.0.2'
import qiskit_aer

qiskit_aer.__version__
'0.17.1'
import qiskit_ibm_runtime

qiskit_ibm_runtime.__version__
'0.40.1'

2. Шумова симуляція без пом'якшення помилок

Qiskit Aer — це класичний симулятор квантових обчислень. Він може моделювати не лише ідеальне, а й зашумлене виконання квантових схем. У цьому блокноті показано, як запустити шумову симуляцію за допомогою Qiskit Aer:

  1. Побудувати модель шуму
  2. Побудувати зашумлений семплер (симулятор) з моделлю шуму
  3. Запустити квантову схему на зашумленому семплері
noise_model = NoiseModel()
...
noisy_sampler = Sampler(options={"backend_options": {"noise_model": noise_model}})
job = noisy_sampler.run([circuit])

2.1 Побудова тестової схеми

Розглянемо прості однокубітні схеми, які просто повторюють гейти X d разів (d=0 ... 100) і вимірюють спостережувану Z.

from qiskit.circuit import QuantumCircuit

MAX_DEPTH = 100
circuits = []
for d in range(MAX_DEPTH + 1):
circ = QuantumCircuit(1)
for _ in range(d):
circ.x(0)
circ.barrier(0)
circ.measure_all()
circuits.append(circ)

display(circuits[3].draw(output="mpl"))

Output of the previous code cell

from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp.from_list([("Z", 1.0)])
obs
SparsePauliOp(['Z'],
coeffs=[1.+0.j])

2.2 Побудова моделі шуму

Для шумової симуляції потрібно задати NoiseModel. У цьому розділі показано, як побудувати NoiseModel. Спочатку треба визначити квантові (або readout) помилки, які додаватимуться до моделі шуму.

from qiskit_aer.noise.errors import (
coherent_unitary_error,
amplitude_damping_error,
ReadoutError,
)
from qiskit.circuit.library import RXGate

# Coherent (unitary) error: Over X-rotation error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.coherent_unitary_error.html#qiskit_aer.noise.coherent_unitary_error
OVER_ROTATION_ANGLE = 0.05
coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())

# Incoherent error: Amplitude dumping error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.amplitude_damping_error.html#qiskit_aer.noise.amplitude_damping_error
AMPLITUDE_DAMPING_PARAM = 0.02 # in [0, 1] (0: no error)
incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)

# Readout (measurement) error: Readout error
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.ReadoutError.html#qiskit_aer.noise.ReadoutError
PREP0_MEAS1 = 0.03 # P(1|0): Probability of preparing 0 and measuring 1
PREP1_MEAS0 = 0.08 # P(0|1): Probability of preparing 1 and measuring 0
readout_error = ReadoutError(
[[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]
)
from qiskit_aer.noise import NoiseModel

noise_model = NoiseModel()
noise_model.add_quantum_error(coherent_error.compose(incoherent_error), "x", (0,))
noise_model.add_readout_error(readout_error, (0,))

2.3 Побудова зашумленого семплера з моделлю шуму

from qiskit_aer.primitives import SamplerV2 as Sampler

noisy_sampler = Sampler(options={"backend_options": {"noise_model": noise_model}})

2.4 Запуск квантових схем на зашумленому семплері

job = noisy_sampler.run(circuits, shots=400)
result = job.result()
result[0].data.meas.get_counts()
{'0': 389, '1': 11}

2.5 Візуалізація результатів

import matplotlib.pyplot as plt

plt.title("Noisy simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
ds,
[result[d].data.meas.expectation_values(["Z"]) for d in ds],
color="gray",
linestyle="-",
)
plt.scatter(ds, [result[d].data.meas.expectation_values(["Z"]) for d in ds], marker="o")
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Circuit depth")
plt.ylabel("Measured <Z>")
plt.show()

2.6 Ідеальна симуляція

ideal_sampler = Sampler()
job_ideal = ideal_sampler.run(circuits)
result_ideal = job_ideal.result()
plt.title("Ideal simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
ds,
[result_ideal[d].data.meas.expectation_values(["Z"]) for d in ds],
color="gray",
linestyle="-",
)
plt.scatter(
ds, [result_ideal[d].data.meas.expectation_values(["Z"]) for d in ds], marker="o"
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.xlabel("Circuit depth")
plt.ylabel("Measured <Z>")
plt.show()

Output of the previous code cell

2.7 Завдання

Змінюючи код нижче:

  • Спробуй збільшити кількість вимірювань у 25 разів (= 10_000 shots) і переконайся, що графік стає плавнішим
  • Змінюй параметри шуму (OVER_ROTATION_ANGLE, AMPLITUDE_DAMPING_PARAM, PREP0_MEAS1 або PREP1_MEAS0) і спостерігай, як змінюється графік
OVER_ROTATION_ANGLE = 0.05
coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())
AMPLITUDE_DAMPING_PARAM = 0.02 # in [0, 1] (0: no error)
incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)
PREP0_MEAS1 = 0.1 # P(1|0): Probability of preparing 0 and measuring 1
PREP1_MEAS0 = 0.05 # P(0|1): Probability of preparing 1 and measuring 0
readout_error = ReadoutError(
[[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]
)
noise_model = NoiseModel()
noise_model.add_quantum_error(coherent_error.compose(incoherent_error), "x", (0,))
noise_model.add_readout_error(readout_error, (0,))
options = {
"backend_options": {"noise_model": noise_model},
}
noisy_sampler = Sampler(options=options)
job = noisy_sampler.run(circuits, shots=400)
result = job.result()
plt.title("Noisy simulation")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
ds,
[result[d].data.meas.expectation_values(["Z"]) for d in ds],
marker="o",
linestyle="-",
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Depth")
plt.ylabel("Measured <Z>")
plt.show()

Output of the previous code cell

2.8 Реалістичніша шумова симуляція

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import SamplerV2 as Sampler, QiskitRuntimeService

service = QiskitRuntimeService()
real_backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
<IBMBackend('ibm_strasbourg')>
aer = AerSimulator.from_backend(real_backend)
noisy_sampler = Sampler(mode=aer)
job = noisy_sampler.run(circuits)
result = job.result()
plt.title("Noisy simulation with noise model from real backend")
ds = list(range(MAX_DEPTH + 1))
plt.plot(
ds,
[result[d].data.meas.expectation_values(["Z"]) for d in ds],
marker="o",
linestyle="-",
)
plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors="black")
plt.ylim(-1, 1)
plt.xlabel("Depth")
plt.ylabel("Measured <Z>")
plt.show()

Output of the previous code cell

3. Реальні квантові обчислення з пом'якшенням помилок

У цій частині ми демонструємо, як отримати результати з пом'якшенням помилок (очікувані значення) за допомогою Qiskit Estimator. Розглядаємо 6-кубітні схеми Троттеризації для симуляції часової еволюції одновимірної моделі Ізінга і дивимося, як масштабується помилка відносно кількості часових кроків.

backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
) # Eagle
backend
<IBMBackend('ibm_strasbourg')>
NUM_QUBITS = 6
NUM_TIME_STEPS = list(range(8))
RX_ANGLE = 0.1
RZZ_ANGLE = 0.1

3.1 Побудова схем

# Build circuits with different number of time steps
circuits = []
for n_steps in NUM_TIME_STEPS:
circ = QuantumCircuit(NUM_QUBITS)
for i in range(n_steps):
# rx layer
for q in range(NUM_QUBITS):
circ.rx(RX_ANGLE, q)
# 1st rzz layer
for q in range(1, NUM_QUBITS - 1, 2):
circ.rzz(RZZ_ANGLE, q, q + 1)
# 2nd rzz layer
for q in range(0, NUM_QUBITS - 1, 2):
circ.rzz(RZZ_ANGLE, q, q + 1)
circ.barrier() # need not to optimize the circuit
# Uncompute stage
for i in range(n_steps):
for q in range(0, NUM_QUBITS - 1, 2):
circ.rzz(-RZZ_ANGLE, q, q + 1)
for q in range(1, NUM_QUBITS - 1, 2):
circ.rzz(-RZZ_ANGLE, q, q + 1)
for q in range(NUM_QUBITS):
circ.rx(-RX_ANGLE, q)
circuits.append(circ)

Щоб заздалегідь знати ідеальний результат, ми використовуємо схеми compute-uncompute, які складаються з першого етапу, де застосовується оригінальна схема UU, і другого етапу, де вона обертається UU^\dagger. Зауваж, що ідеальним результатом таких схем буде тривіально вхідний стан 000000|000000\rangle, який має тривіальні очікувані значення для будь-яких спостережуваних Паулі, наприклад IIIIIZ=1\langle IIIIIZ \rangle = 1.

# Print the circuit with 2 time steps
circuits[2].draw(output="mpl")

Output of the previous code cell

Примітка: як показано вище, схема з kk часовими кроками матиме 4k4k шарів двокубітних гейтів.

obs = SparsePauliOp.from_sparse_list([("Z", [0], 1.0)], num_qubits=NUM_QUBITS)
obs
SparsePauliOp(['IIIIIZ'],
coeffs=[1.+0.j])

3.2 Транспіляція схем

Транспілюємо схеми для бекенду з оптимізацією (optimization_level=1).

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuits = pm.run(circuits)
display(isa_circuits[2].draw("mpl", idle_wires=False, fold=-1))

Вивід попередньої комірки коду

3.3 Виконання за допомогою Estimator (з різними рівнями стійкості)

Налаштування рівня стійкості (estimator.options.resilience_level) — це найпростіший спосіб застосувати пом'якшення помилок під час використання Qiskit Estimator. Estimator підтримує такі рівні стійкості (станом на 28.06.2024). Детальніше дивись у посібнику Налаштування пом'якшення помилок.

image.png

from qiskit_ibm_runtime import Batch
from qiskit_ibm_runtime import EstimatorV2 as Estimator

jobs = []
job_ids = []
with Batch(backend=backend):
for resilience_level in [0, 1, 2]:
estimator = Estimator()
estimator.options.resilience_level = resilience_level
job = estimator.run(
[(circ, obs.apply_layout(circ.layout)) for circ in isa_circuits]
)
job_ids.append(job.job_id())
print(f"Job ID (rl={resilience_level}): {job.job_id()}")
jobs.append(job)
Job ID (rl=0): d146vcnmya70008emprg
Job ID (rl=1): d146vdnqf56g0081sva0
Job ID (rl=2): d146ven5z6q00087c61g
# check job status
for job in jobs:
print(job.status())
DONE
DONE
DONE
# REPLACE WITH YOUR OWN JOB IDS
jobs = [service.job(job_id) for job_id in job_ids]
# Get results
results = [job.result() for job in jobs]

3.4 Побудова графіку результатів

plt.title("Error mitigation with different resilience levels")
labels = ["0 (No mitigation)", "1 (TREX)", "2 (ZNE + Gate twirling)"]
steps = NUM_TIME_STEPS
for result, label in zip(results, labels):
plt.errorbar(
x=steps,
y=[result[s].data.evs for s in steps],
yerr=[result[s].data.stds for s in steps],
marker="o",
linestyle="-",
capsize=4,
label=label,
)
plt.hlines(
1.0, min(steps), max(steps), linestyle="dashed", label="Ideal", colors="black"
)
plt.xlabel("Time steps")
plt.ylabel("Mitigated <IIIIIZ>")
plt.legend()
plt.show()

Вивід попередньої комірки коду

4. (Необов'язково) Налаштування параметрів пом'якшення помилок

Ми можемо налаштувати застосування технік пом'якшення помилок через параметри, як показано нижче.

# TREX
estimator.options.twirling.enable_measure = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.twirling.shots_per_randomization = "auto"

# Gate twirling
estimator.options.twirling.enable_gates = True
# ZNE
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = [1, 3, 5]
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")

# Dynamical decoupling
estimator.options.dynamical_decoupling.enable = True # Default: False
estimator.options.dynamical_decoupling.sequence_type = "XX"

# Other options
estimator.options.default_shots = 10_000

Детальніше про параметри пом'якшення помилок дивись у таких посібниках та довіднику API.

Source: IBM Quantum docs — updated 27 квіт. 2026 р.
English version on doQumentation — updated 7 трав. 2026 р.
This translation based on the English version of approx. 20 бер. 2026 р.