Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Як посилити межі за допомогою обмеження швидкості

Інформація в схемі двокубітних вентилів не може поширюватися довільно швидко. Помилка, що виникла за кілька шарів до кінця, на кубіті далеко від носія спостережуваної, не має шляху, щоб до неї дістатися. tighten_with_speed_limit перетворює цей геометричний аргумент (ту саму інтуїцію, що стоїть за межами Ліб-Робінсона) на жорсткіші прямі межі.

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

Спершу варто відповісти на питання «коли це допомагає?», адже на повністю збіжному наборі прямих меж воно допомагає нечасто.

Налаштування​

Використай ту саму схему Ізінга з тротеризацією, що й у посібнику зі швидкого старту, зі спостережуваною на одному кубіті посередині ланцюжка. 1D-геометрія полегшує міркування: вплив має пройти ланцюжок по одному зв'язку на шар.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_slc": "qiskit-addon-slc", "samplomatic": "samplomatic"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli
from qiskit_addon_slc.bounds import (
compute_forward_bounds,
tighten_with_speed_limit,
)
from qiskit_addon_slc.utils import generate_noise_model_paulis
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):
"""Trotterized transverse-field Ising evolution on a 1D chain."""
circuit = QuantumCircuit(num_qubits)
for _ in range(num_steps):
circuit.rx(rx_angle, range(num_qubits))
circuit.barrier()
for start in (0, 1): # even then odd bonds
for i in range(start, num_qubits - 1, 2):
circuit.rzz(rzz_angle, i, i + 1)
circuit.barrier()
return circuit

num_qubits = 10
circuit = trotter_ising_circuit(
num_qubits, num_steps=4, rx_angle=np.pi / 16, rzz_angle=-np.pi / 3
)

# A single-qubit observable in the middle of the chain.
observable = Pauli("I" * num_qubits).compose("Z", [num_qubits // 2])

boxing_pass = generate_boxing_pass_manager(
inject_noise_targets="all",
inject_noise_strategy="individual_modification",
inject_noise_site="after",
twirling_strategy="active",
remove_barriers="never",
)
boxed_circuit = boxing_pass.run(circuit)
noise_model_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_circuit)
)

def compare(label, bounds):
"""Tightens a set of bounds and prints what changed."""
tightened = tighten_with_speed_limit(
bounds, boxed_circuit, noise_model_paulis, observable
)
before = np.concatenate([bound.rates for bound in bounds.values()])
after = np.concatenate([bound.rates for bound in tightened.values()])
print(
f"{label:30} mean {before.mean():.4f} -> {after.mean():.4f} "
f"tightened {np.count_nonzero(after < before - 1e-12):4} of {len(before)} "
f"loosened {np.count_nonzero(after > before + 1e-12)}"
)

print(
f"observable: Z on qubit {num_qubits // 2}, circuit depth {circuit.depth()}"
)
observable: Z on qubit 5, circuit depth 12

Коли це допомагає, а коли ні​

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

# 1. Fully computed forward bounds.
full_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable
)
compare("fully computed", full_bounds)

# 2. Bounds where the eigenvalue step was skipped, leaving the looser triangle inequality.
loose_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable, eigval_max_qubits=0
)
compare("triangle inequality only", loose_bounds)

# 3. Bounds computed only for the first few boxes, the rest left at the trivial bound of 2.0.
shallow_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable, max_num_boxes=2
)
compare("max_num_boxes=2", shallow_bounds)
fully computed mean 0.3639 -> 0.3639 tightened 0 of 792 loosened 0
triangle inequality only mean 0.5081 -> 0.4842 tightened 105 of 792 loosened 0
max_num_boxes=2 mean 1.5707 -> 0.5284 tightened 450 of 792 loosened 0

Перший рядок — той, на який варто звернути увагу. На повністю обчислених прямих межах обмеження швидкості нічого не змінює. Це не збій. Це означає, що межі вже були принаймні такими ж жорсткими, як може довести геометричний аргумент, бо еволюція оператора фактично сама відкрила світловий конус.

Виграш з'являється там, де межі були обчислені не повністю:

  • eigval_max_qubits=0 примусово вмикає запасний варіант із нерівністю трикутника, тож межі більше не відображають справжню спектральну норму комутатора. Обмеження швидкості відновлює частину цієї втрати.

  • max_num_boxes=2 залишає більшість шарів на тривіальній межі 2.0 (межа, що не кодує жодної інформації). Тут обмеження швидкості корисне, скорочуючи середнє приблизно втричі.

У кожному випадку стовпець «послаблено» нульовий. Жоден член ніколи не погіршується, і це гарантія, яка робить крок безпечним для безумовного застосування.

Що це дає для накладних витрат вибірки​

Жорсткіші межі означають менше членів помилок, які потрібно пом'якшувати для заданого допуску зміщення, а саме це число має значення на обладнанні.

from qiskit.quantum_info import PauliLindbladMap, QubitSparsePauliList
from qiskit_addon_slc.bounds import compute_local_scales

# Stand-in for rates that a noise-learning experiment would measure on hardware.
rng = np.random.default_rng(42)
noise_rates = {
layer: PauliLindbladMap.from_components(
rng.random(len(terms)) * 5e-3,
QubitSparsePauliList.from_sparse_list(
terms.to_sparse_list(), terms.num_qubits
),
)
for layer, terms in noise_model_paulis.items()
}

tightened_shallow = tighten_with_speed_limit(
shallow_bounds, boxed_circuit, noise_model_paulis, observable
)

print(
f"{'bounds':30} {'overhead':>9} {'mitigated':>10} {'residual bias':>14}"
)
for label, bounds in (
("max_num_boxes=2", shallow_bounds),
("max_num_boxes=2 + speed limit", tightened_shallow),
("fully computed", full_bounds),
):
scales, cost, bias = compute_local_scales(
boxed_circuit, bounds, noise_rates, bias_tolerance=0.05
)
mitigated = sum(int(np.count_nonzero(scale)) for scale in scales.values())
total = sum(len(scale) for scale in scales.values())
print(
f"{label:30} {cost:9.4f} {f'{mitigated}/{total}':>10} {bias:>14.4f}"
)
bounds overhead mitigated residual bias
max_num_boxes=2 420.2823 617/792 0.0490
max_num_boxes=2 + speed limit 7.8511 226/792 0.0485
fully computed 5.4031 188/792 0.0494

Коли це використовувати​

Застосовуй його до прямих меж щоразу, коли ці межі не повністю збіжні (по суті, щоразу, коли ти проміняв точність на швидкість, використавши будь-який із параметрів у посібнику з конфігурації). Він дешевий порівняно з обчисленням меж, не може нічого погіршити та відновлює багато з того, що віддав ліміт max_num_boxes або eigval_max_qubits.

На повністю обчислених межах він нічого не робить, тож безумовне застосування не шкодить.

Два обмеження:

  • Він застосовується лише до прямих меж. Зворотні межі стосуються впливу помилки на початковий стан, а не на спостережувану, тож аргумент не переноситься.

  • Спостережувана має бути одним членом Паулі. Для спостережуваної з кількома членами викликай це один раз на кожен оператор Паулі та поєднуй результати самостійно. Якщо спостережувана не є одним членом Паулі, виникає NotImplementedError.

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