Přeskočit na hlavní obsah
Tento projekt je v betě. Našel jsi chybu? Otevřít issue na GitHubu. Máš otázku nebo nápad? Zeptej se v Discussions — rádi si poslechneme tvou zpětnou vazbu!
Jaká je kvalita překladu na této stránce?

Jak zpřesnit meze pomocí rychlostního limitu

Informace v obvodu z dvouqubitových hradel se nemůže šířit libovolně rychle. Chyba, která nastane několik vrstev před koncem na qubitu daleko od nosiče observable, se k ní nemá jak dostat. tighten_with_speed_limit mění tuto geometrickou úvahu (stejnou intuici, jakou mají Lieb-Robinsonovy meze) na těsnější dopředné meze.

Tento krok je levný, nepotřebuje nic nad rámec toho, co dopředné meze už vyžadovaly, a může meze pouze zpřesnit. Není třeba zvažovat žádný kompromis v přesnosti.

Nejdřív stojí za to odpovědět na otázku „kdy to pomáhá?“, protože u plně zkonvergované sady dopředných mezí to často nepomáhá.

Nastavení​

Použij stejný trotterizovaný Isingův obvod jako v rychlém startu, s observable na jediném qubitu uprostřed řetězce. 1D geometrie usnadňuje úvahy o efektu: vliv musí řetězcem projít po jedné vazbě na vrstvu.

# 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

Kdy to pomáhá a kdy ne​

tighten_with_speed_limit vezme meze, které už máš, a vrátí zpřesněnou kopii; tvé původní meze se nikdy nezmění. Kolik získá, závisí zcela na tom, jak volný byl vstup.

# 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

První řádek stojí za povšimnutí. U plně vypočtených dopředných mezí rychlostní limit nemění nic. To není selhání. Znamená to, že meze už byly alespoň tak těsné, jak dokáže geometrická úvaha dokázat, protože vývoj operátoru si světelný kužel fakticky objevil sám.

Zisky se objevují tam, kde meze nebyly plně vypočteny:

  • eigval_max_qubits=0 vynutí náhradní postup s trojúhelníkovou nerovností, takže meze už neodrážejí skutečnou spektrální normu komutátoru. Rychlostní limit část této ztráty napraví.

  • max_num_boxes=2 ponechá většinu vrstev na triviální mezi 2,0 (mez, která nenese žádnou informaci). Zde rychlostní limit pomáhá a snižuje průměr zhruba třikrát.

Ve všech případech je sloupec „zhoršeno“ nulový. Žádný člen se nikdy nezhorší, což je záruka, díky níž je krok bezpečné aplikovat bezpodmínečně.

Co přináší ve vzorkovací režii​

Těsnější meze znamenají, že pro danou toleranci vychýlení je méně chybových členů, které je třeba mitigovat, a to je číslo, na kterém na hardwaru záleží.

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

Kdy ho použít​

Aplikuj ho na dopředné meze vždy, když tyto meze nejsou plně zkonvergované (v zásadě kdykoli jsi výměnou za rychlost obětoval přesnost použitím kterékoli z voleb v konfiguračním průvodci). Je levný ve srovnání s výpočtem mezí, nemůže nic zhoršit a získá zpět velkou část toho, co se vzdal limit max_num_boxes nebo eigval_max_qubits.

U plně vypočtených mezí je to prázdná operace, takže nevadí ho aplikovat bezpodmínečně.

Dvě omezení:

  • Platí pouze pro dopředné meze. Zpětné meze se týkají vlivu chyby na počáteční stav, a ne na observable, takže úvaha se nepřenáší.

  • Observable musí být jediný Pauliho člen. U observable s více členy zavolej funkci jednou pro každý Pauliho člen a výsledky spoj sám. Pokud observable není jediný Pauliho člen, vyvolá NotImplementedError.

Ve standardním pracovním postupu patří mezi výpočet dopředných mezí a jejich sloučení se zpětnými mezemi, jak ukazuje rychlý start.