Paano i-tune ang mga opsyon sa configuration
Ang pagkalkula ng mga bound ay eksakto sa prinsipyo pero approximate sa praktika: ang pag-evolve ng isang Pauli error term sa isang circuit ay gumagawa ng operator na may exponentially na dami ng term, at ang paghahanap ng spectral norm ng nagreresultang commutator ay magastos din mismo. Kinokontrol ng mga opsyon sa ibaba ang gastos na iyon. Nahahati sila sa tatlong grupo:
-
Binabago ng mga opsyon sa Accuracy ang numerikal na halaga ng mga bound, kapalit ng higpit para sa mas mababang gastos.
-
Tinutukoy ng Scope kung aling mga bound ang kakalkulahin.
-
Binabago ng mga opsyon sa Execution kung paano tumatakbo ang pagkalkula (hindi kung ano ang kinakalkula nito).
| Opsyon | Default | Kinokontrol | Epekto sa resulta |
|---|---|---|---|
| Accuracy | |||
evolution_max_terms | 1_000_000 | Ilang operator term ang natitira pagkatapos ng evolution | Mas mababa: mas mura, mas maluwag na mga bound |
atol_simplify | 1e-8 | Aling mga commutator term ang itinatapon bilang bale-wala | Mas mataas: mas mura, mas maluwag na mga bound |
eigval_max_qubits | 14 | Gaano kalapad ang commutator para makalkula ang spectral norm nito | Mas mababa: mas mura, mas maluwag na mga bound |
atol_eigenvalue | 1e-10 | Kailan ituturing na nag-converge ang iterative eigensolver | Mas mataas: mas mura, mas di-tumpak na mga bound |
| Scope | |||
max_num_boxes | None | Ilang box ang kakalkulahan ng mga bound | Ang mga box na lampas sa limitasyon ay nananatili sa trivial na bound na 2.0 |
| Execution | |||
timeout | None | Isang wall-clock na badyet para sa buong pagkalkula | Anumang natitirang trabaho ay nananatili sa trivial na bound na 2.0 |
num_processes | 1 | Ilang worker process ang sabay-sabay na kumakalkula ng mga bound | Wala sa sarili nito |
Kapaki-pakinabang lang ang isang bound kung valid ito: hindi nito dapat maliitin ang epekto ng isang error term sa
observable. Ang pagluwag sa alinman sa mga opsyong ito maliban sa atol_eigenvalue ay makakapagpalaki lang sa mga bound
(mas konserbatibo), hindi kailanman magpapaliit. Kaya, ang kapalit ng maluwag na setting ay mas malaking sampling
overhead, hindi maling expectation value - ang atol_eigenvalue ang eksepsiyon.
Kahulugan ng circuit
Gumagamit ang mga sumusunod na eksperimento ng parehong Trotterized Ising circuit at observable gaya ng quickstart guide. Sa laking ito, may nakikitang epekto ang bawat opsyon habang wala pang isang segundo ang bawat pagkalkula.
# 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.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
)
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)
)
print(f"noisy layers: {len(noise_model_paulis)}")
print(
f"Pauli error terms: {sum(len(p) for p in noise_model_paulis.values())}"
)
noisy layers: 2
Pauli error terms: 198
Ibinubuod ng isang maliit na helper ang bawat set ng mga bound sa isang linya, kaya ang epekto ng isang opsyon ay lalabas bilang ilang numero sa halip na isang pader ng mga array.
Bawat bound ay nasa pagitan ng 0 at ng trivial na bound na 2.0. Iniuulat ng buod ang mean na bound at ang standard deviation nito, kasunod kung paano nahahati ang mga term sa mga grupong ito:
- Mga term na may bound na eksaktong 0 dahil nagko-commute sila sa na-evolve na observable
- Mga term na may nakalkulang bound sa pagitan ng 0 at 2.0
- Mga term na nanatili sa trivial na 2.0
Tandaan na ang mas mababang mean ay mas mahigpit, at ang mas mahigpit ay nangangahulugang mas maliit na sampling overhead sa dakong huli.
Minamarkahan ng sweep sa isang opsyon ang default ng opsyon gamit ang *:
import inspect
import time
from qiskit_addon_slc.bounds import compute_forward_bounds
def summarize(rates, label, elapsed):
"""Prints a one-line summary of a set of bounds."""
# The bounds are floating-point results, so a term counts as bounded by 0, or as left at the
# trivial 2.0, when it is within rounding error of that value.
exact = np.isclose(rates, 0.0, rtol=0.0, atol=1e-12)
trivial = np.isclose(rates, 2.0, rtol=0.0, atol=1e-12)
computed = ~exact & ~trivial
print(
f"{label:28} mean {rates.mean():.4f} ± {rates.std():.3f} │ "
f"at 0: {exact.mean():4.0%} computed: {computed.mean():4.0%} "
f"at 2.0: {trivial.mean():4.0%} │ "
f"{elapsed:4.2f}s"
)
def forward(label, **kwargs):
"""Computes the forward bounds, summarizes them and returns all their rates."""
start = time.perf_counter()
bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable, **kwargs
)
elapsed = time.perf_counter() - start
rates = np.concatenate([bound.rates for bound in bounds.values()])
summarize(rates, label, elapsed)
return rates
def sweep(option, values):
"""Computes the forward bounds for each value of one option, marking the default with `*`."""
default = (
inspect.signature(compute_forward_bounds).parameters[option].default
)
for value in values:
marker = "*" if value == default else " "
forward(f"{marker} {option}={value:g}", **{option: value})
baseline = forward(" defaults")
defaults mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.29s
Katumpakan
evolution_max_terms
Ang pag-evolve ng Pauli error term sa mga non-Clifford gate ay ginagawa itong kabuuan ng mga Pauli, at lumalaki ang kabuuang iyon
kasabay ng lalim ng circuit. Nililimitahan ng evolution_max_terms kung ilan sa mga term na iyon ang itatabi. Ang 1-norm ng
anumang itinapon ay idinaragdag sa bound, kaya walang tahimik na nawawala.
Tumatakbo ang sweep mula sa mas mahigpit kaysa sa default pababa sa matinding pagputol. Ang pagbaba ng cap ay ginagawang mas mura ang pagkalkula at mas maluwag ang mga bound.
sweep("evolution_max_terms", (10_000_000, 1_000_000, 1000, 100, 10, 2))
evolution_max_terms=1e+07 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.38s
* evolution_max_terms=1e+06 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
evolution_max_terms=1000 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.30s
evolution_max_terms=100 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.29s
evolution_max_terms=10 mean 0.3927 ± 0.711 │ at 0: 59% computed: 31% at 2.0: 9% │ 0.28s
evolution_max_terms=2 mean 0.5245 ± 0.792 │ at 0: 59% computed: 23% at 2.0: 18% │ 0.21s
Ang pagtaas ng cap nang lampas sa default ay walang binabago dito. Para sa circuit na ganito kalaki, ang default ay nagtatabi na ng bawat term, kaya ang mga bound ay kasinghigpit na ng magagawa ng opsyong ito. Iyon ang tipikal na sitwasyon. Ang paghigpit sa isang accuracy option ay may saysay lang kapag ang default nito ay talagang naglilimita, at ang paraan para malaman iyon ay ang eksperimento sa itaas. Patakbuhin ulit ito gamit ang mas mahigpit na halaga at tingnan kung gumalaw ang mga bound.
Babala: Itinuturing ng pinagbabatayang propagation routine ang evolution_max_terms bilang capacity na
i-pre-allocate, hindi bilang upper limit na susuriin. Ang pagpasa ng nominal na walang limitasyon, gaya
ng numpy.iinfo(numpy.uint).max, ay humihingi sa allocator ng terabytes ng memory at ina-abort ang process.
Panatilihin ito sa makatotohanang numero. Sapat na ang default na isang milyon.
atol_simplify
Pagkabuo ng commutator, itinatapon ang mga term na may coefficient na mas mababa sa atol_simplify. Gaya ng
evolution cap, ang 1-norm na nawala sa ganitong paraan ay idinaragdag sa bound.
sweep("atol_simplify", (0.0, 1e-8, 1e-4, 1e-2, 1e-1))
atol_simplify=0 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.32s
* atol_simplify=1e-08 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
atol_simplify=0.0001 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.33s
atol_simplify=0.01 mean 0.3656 ± 0.685 │ at 0: 59% computed: 35% at 2.0: 6% │ 0.31s
atol_simplify=0.1 mean 0.3837 ± 0.697 │ at 0: 59% computed: 31% at 2.0: 10% │ 0.31s
eigval_max_qubits
Ang pinakamahigpit na magagamit na bound ay ang spectral norm ng commutator, na nangangailangan ng eigenvalue
na pagkalkula. Nagiging magastos iyon habang lumalapad ang commutator, kaya itinatakda ng eigval_max_qubits ang lapad
na lampas dito ay iiwan ang pagtatangka pabor sa triangle inequality. Ang bound na iyon,
sa mga coefficient ng commutator, ay valid pero mas maluwag.
Ang pagtatakda nito sa zero ay pumipilit sa fallback saanman, na nagpapakita ng benepisyo ng eigensolver sa iisang numero.
sweep("eigval_max_qubits", (20, 14, 8, 4, 2, 0))
eigval_max_qubits=20 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
* eigval_max_qubits=14 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.30s
eigval_max_qubits=8 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.32s
eigval_max_qubits=4 mean 0.3742 ± 0.694 │ at 0: 59% computed: 35% at 2.0: 6% │ 0.30s
eigval_max_qubits=2 mean 0.4983 ± 0.809 │ at 0: 59% computed: 23% at 2.0: 18% │ 0.27s
eigval_max_qubits=0 mean 0.5081 ± 0.810 │ at 0: 59% computed: 21% at 2.0: 19% │ 0.27s
Kahit naka-off ang eigensolver, nananatiling mas mababa nang malayo ang mean sa trivial na 2.0. Ang bahagi ng mga term na may bound na 0 ay hindi nagbabago: ang term na eksaktong nagko-commute sa na-evolve na observable ay may zero na commutator, na nakikilala bago pa isaalang-alang ang anumang eigenvalue na pagkalkula. Sa iba, ang triangle-inequality bound ay umaabot sa 2.0 para sa ilan at doon na-cap, pero para sa marami ay mas maliit pa rin ito, kaya nananatili silang may nakalkulang bound na mas mababa sa 2.0.
atol_eigenvalue
Kapag kinakalkula ang spectral norm, ang atol_eigenvalue ang convergence tolerance ng
iterative eigensolver. Karamihan ng mga commutator ay hindi umaabot sa solver na iyon, gayunpaman:
-
Ang commutator na kumikilos sa hindi hihigit sa apat na qubit ay dinidiagonalize nang eksakto, direkta.
-
Ang mas malapad ay unang binabawasan sa pinakamaliit na bilang ng mga qubit na kailangan ng mga Pauli term nito, at kung ang nabawasang anyo ay kumikilos sa hindi hihigit sa walong qubit, dinidiagonalize ito nang eksakto.
Tanging ang commutator na nangangailangan pa rin ng higit sa walong qubit pagkatapos ng pagbabawas na iyon ang pupunta sa iterative solver, na siyang tanging lugar kung saan nalalapat ang tolerance na ito. Ang mga circuit na kasinglaki ng gamit sa gabay na ito ay hindi kailanman umaabot doon, kaya ang sweep sa opsyong ito ay magpapakita ng magkakaparehong bound para sa bawat halaga. Mahalaga ito para sa malalapad na light cone sa malalaking device.
Hindi tulad ng ibang accuracy option, ang pagluwag sa atol_eigenvalue ay hindi ligtas sa isang direksyon. Ang
iterative eigensolver ay lumalapit sa extremal na eigenvalue mula sa loob ng spectrum, kaya ang estimate
na hindi pa ganap na nag-converge ay nagmamaliit sa spectral norm, at sa bound. Sadyang mahigpit ang default
dahil doon. Kung hindi mag-converge ang solver, papalit ang triangle-inequality
fallback, na valid ulit.
Saklaw
max_num_boxes
Minsan ang kapaki-pakinabang na limitasyon ay hindi accuracy kundi lalim. Ang max_num_boxes ay humihinto sa pagsisimula ng mga bound
na pagkalkula lampas sa isang tiyak na bilang ng mga box, at bawat layer na lampas dito ay binibigyan ng trivial na bound
na 2.0 sa halip.
Binibilang ng limitasyon ang mga maingay na BoxOp na instruction (yaong may dalang InjectNoise annotation) sa halip
na, halimbawa, lalim sa mga gate. Pinapanatili nitong nakatali ito sa istrukturang nakikita ng user ng circuit at
iniiwasan ang pagkakabit sa mga auto-generated na annotation reference, na maaaring magbago sa pagitan ng
mga transpilation run.
Ito ang natural na pagpipilian para sa circuit na ang dalawang kalahati ay hinahawakan ng forward at backward na mga bound ayon sa pagkakasunod: kalkulahin ang bawat isa hanggang sa lalim lang na gagamitin.
from qiskit.circuit import BoxOp
from samplomatic import InjectNoise
# The limit counts the noisy boxes encountered while iterating the circuit, not the unique ones:
# a repeated layer is one noise model but several boxes. Boxes without an `InjectNoise`
# annotation carry no error terms and do not count.
num_boxes = sum(
1
for inst in boxed_circuit.data
if isinstance(inst.operation, BoxOp)
and any(
isinstance(annotation, InjectNoise)
for annotation in inst.operation.annotations
)
)
print(
f"the circuit has {num_boxes} noisy boxes drawn from "
f"{len(noise_model_paulis)} distinct noise models\n"
)
for limit in (None, 6, 4, 2, 1):
marker = "*" if limit is None else " "
forward(f"{marker} max_num_boxes={limit}", max_num_boxes=limit)
the circuit has 9 noisy boxes drawn from 2 distinct noise models
* max_num_boxes=None mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.33s
max_num_boxes=6 mean 0.7486 ± 0.935 │ at 0: 53% computed: 18% at 2.0: 30% │ 0.18s
max_num_boxes=4 mean 1.1506 ± 0.980 │ at 0: 40% computed: 5% at 2.0: 55% │ 0.11s
max_num_boxes=2 mean 1.5707 ± 0.821 │ at 0: 21% computed: 0% at 2.0: 79% │ 0.05s
max_num_boxes=1 mean 1.8157 ± 0.579 │ at 0: 9% computed: 0% at 2.0: 91% │ 0.03s
Pagpapatupad
timeout
Ang timeout ay wall-clock na badyet sa mga segundo. Kapag nag-expire ito, itatabi ang mga bound na nakalkula na
at ang lahat ng nakabinbin ay mananatili sa trivial na bound na 2.0, kaya laging magagamit ang resulta, mas
maluwag lang kapag mas maaga mo itong pinutol.
Hindi ito mahigpit na deadline: pinapayagang matapos ang mga gawaing tumatakbo na, kaya maaaring lumampas ang tawag sa badyet nang hanggang tagal ng isang gawain.
num_processes
Ang mga bound para sa magkakaibang error term ay independent, kaya ipinamamahagi sila ng num_processes sa mga worker
process. Sa sarili nito, ang naaapektuhan lang ay kung gaano katagal ang pagkalkula, hindi kailanman ang resulta.
Tandaan: Ang timeout na isinama sa num_processes ay nagpapadepende sa resulta sa timing: kung aling mga gawain
ang matatapos bago mag-expire ang badyet ay hindi deterministic. Nananatiling valid ang mga bound, pero hindi na sila
mauulit sa bawat run.
Saan magsisimula
Pinili ang mga default para magbigay ng mahigpit na mga bound sa mga circuit na may sukat kung saan abot-kaya iyon. Kapag masyadong mabagal ang isang pagkalkula, subukang ilapat ang mga opsyon sa humigit-kumulang na ganitong pagkakasunod:
-
num_processes: Libreng pagpapabilis, walang gastos sa accuracy. -
max_num_boxes: Kung alam mo na nang maaga kung gaano kalalim sa circuit ang mahalaga sa iyo. -
eigval_max_qubits: Nilalaktawan ang pinakamahal na hakbang para sa pinakamalalapad na commutator, na karaniwang maliit na minorya ng mga term. -
evolution_max_termsatatol_simplify: Nakakaapekto ang mga ito sa accuracy sa kabuuan, kaya i-tune ang mga ito sa huli, at suriin gamit ang sweep na tulad ng nasa itaas para malaman kung gaano gumagalaw ang mga bound. -
timeout: Isang hard stop sa halip na tulong sa pag-tune, para kapag kailangang matapos ang pagkalkula bago ang deadline, anuman ang kalidad.
Habang mas maluwag ang mga bound, mas malaki ang sampling overhead na iuulat ng compute_local_scales para sa
ibinigay na bias tolerance. Ang overhead na iyon ang dapat bantayan habang nagt-tune: sinasabi nito kung ano
ang aktwal na halaga ng mga approximation.