Trunchierea termenilor Pauli în timpul retropropagării
Acest ghid explică cum să configurezi mecanismul de trunchiere a termenilor Pauli oferit de modulul qiskit_addon_obp.utils.truncating.
Operator backpropagation (OBP) poate fi folosit pentru a reduce adâncimea circuitelor cuantice în schimbul unei observabile mai complexe. Pentru a obține rezultate semnificative din OBP, de obicei este nevoie să trunchiezi termeni din observabilă pentru a o împiedica să crească prea mult. O modalitate de a permite o retropropagare mai adâncă în circuit, evitând în același timp ca operatorul să crească prea mult, este trunchierea termenilor cu coeficienți mici, în loc de a-i adăuga la operator. Trunchierea termenilor poate duce la mai puține circuite cuantice de executat, dar generează o anumită eroare în calculul final al valorii de așteptare, proporțională cu magnitudinea coeficienților termenilor trunchiați.
Metoda backpropagate primește opțional un TruncationErrorBudget, care configurează trunchierea termenilor Pauli de pondere mică pentru fiecare observabilă după retropropagarea cu succes a fiecărei felii. Numărul de termeni trunchiați depinde de diverși parametri de configurare specificați de utilizator. În prezent este disponibilă o singură strategie de trunchiere - metoda truncate_binary_search. Dată o observabilă și un buget, aceasta efectuează o căutare binară printre termenii Pauli și coeficienții din acea observabilă pentru a găsi pragul optim, astfel încât suma coeficienților trunchiați să fie maximă, dar mai mică decât bugetul.
Notă: Implicit, norma L1 este folosită pentru a evalua și a mărgini eroarea de trunchiere; totuși, setarea p_norm permite specificarea normei Lp de utilizat.
Pentru mai multe informații despre cum să folosești această setare, consultă ghidul Use different Lp-norms for Pauli term truncation.
Următoarele exemple ilustrează diverse moduri de a construi un TruncationErrorBudget folosind funcția însoțitoare setup_budget.
Construiește un circuit exemplu
Acest ghid folosește următoarele felii de circuit:
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils", "rustworkx": "rustworkx"}
_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 rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
PauliOrderStrategy,
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
# Generate a linear chain of 10 qubits
linear_chain = rustworkx.generators.path_graph(10)
# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
linear_chain,
coupling_constants=(0.05, 0.02, 0.0),
ext_magnetic_field=(0.02, 0.08, 0.0),
pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)
# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

Ne vom uita la o singură observabilă simplă:
from qiskit.quantum_info import SparsePauliOp
obs = SparsePauliOp("IIIIIZIIII")
Cazul cel mai simplu: un buget de trunchiere fix pentru fiecare felie
Bugetul disponibil pentru trunchierea termenilor Pauli poate varia la fiecare pas al retropropagării. Pentru a înțelege cum funcționează, ne uităm mai întâi la cel mai simplu caz, un buget de trunchiere fix specificat de utilizator.
Cea mai directă modalitate de a specifica bugetul de trunchiere este folosind argumentul max_error_per_slice.
De fapt, aceasta este abordarea din tutorialul Reducing depth of circuits with operator backpropagation. Setarea max_error_per_slice la un float face ca fiecare felie să primească un buget egal cu acea valoare.
În exemplul de mai jos, setăm această valoare la 0.001, ceea ce garantează o eroare de trunchiere implicită de cel mult 0.018, dacă toate cele 18 felii ar fi retropropagate.
Reține că orice buget de eroare rămas după retropropagarea unei felii și trunchierea termenilor cu coeficienți mici va fi întotdeauna adăugat la bugetul de eroare al feliei următoare.
from qiskit_addon_obp.utils.truncating import setup_budget
truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 11 circuit slices.
New observable contains 29 terms and 10 commuting groups.
În continuare folosim instanța OBPMetadata și instrumentele oferite de modulul visualization pentru a vizualiza procesul de retropropagare.
-
Graficul din stânga sus arată că avem suficient buget pentru a începe trunchierea termenilor observabilei după retropropagarea celei de-a treia felii. Începând cu a treia felie, știm că trunchiem cel puțin un termen din fiecare felie retropropagată, deoarece introducem o anumită eroare de trunchiere după fiecare felie.
-
Graficul din dreapta sus arată că bugetul de eroare crește la
.003pentru a treia felie. Vedem o scădere abruptă a bugetului rămas, ceea ce înseamnă că s-au trunchiat termeni din observabilă. Aceasta este în concordanță cu ceea ce am dedus din graficul din stânga sus. -
Graficul din stânga jos arată că, pe măsură ce trunchiem termeni din observabilă, eroarea noastră totală acumulată crește monoton. Acest grafic confirmă și că nu s-au trunchiat termeni până după retropropagarea celei de-a treia felii.
-
Graficul din dreapta jos arată că numărul de grupuri Pauli comutative din observabila noastră a crescut până la limita specificată de
10. Acest grafic arată și cum retropropagarea unui strat în plus ar face observabila să depășească limita specificată, după cum se vede din intersecția liniilor neagră și roșie.
Reține că în toate aceste grafice, axa x enumeră feliile retropropagate, dar, deoarece OBP lucrează la capătul circuitului, slice 1 este chiar ultima felie, slice 2 este cea dinaintea ei și așa mai departe.
from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
plot_accumulated_error,
plot_left_over_error_budget,
plot_num_qwc_groups,
plot_slice_errors,
)
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Specifică explicit bugetul feliilor
Dacă știi cum să aloci un buget fiecărei felii astfel încât performanța retropropagării să fie optimizată, poate vrei să aloci explicit un buget pentru fiecare felie. În scop demonstrativ, vom aloca primelor trei felii un buget zero și vom folosi un buget de .001 pe felie pentru restul feliilor.
# Zero out the first 3 slices' budgets
max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 11 circuit slices.
New observable contains 32 terms and 10 commuting groups.
Eliminarea bugetului din primele trei straturi a dus la netrunchierea niciunui termen până după al patrulea strat, după cum se poate confirma în trei dintre aceste grafice. Poate surprinzător, deși a patra felie nu a primit niciun buget rămas, un termen tot a fost trunchiat folosind bugetul alocat de .001. Acest lucru este evident din graficul din stânga sus, dar poate fi observat și în graficul din dreapta sus, deoarece curba bugetului rămas se aplatizează între feliile trei și patru. Un alt detaliu notabil este că de aici înainte a fost trunchiat cel puțin un termen, la fel ca în exemplul anterior.
Ideea principală este că, deși am introdus o eroare de trunchiere mai mică în al doilea exemplu datorită anulării bugetelor unor felii, am putut retropropaga același număr de felii, iar observabila noastră conține același număr de grupuri Pauli comutative. Putem confirma că marginea erorii noastre este mai mică în al doilea exemplu inspectând graficul din stânga jos.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Specifică bugetul ciclic
Dacă ai un circuit cu un tipar repetabil, cum ar fi un circuit Trotter, poate vrei să specifici un buget pentru acel subset repetat de felii și să folosești acel buget pentru toate repetițiile următoare ale acestor felii.
Mai concret, circuitul exemplu pe care îl folosim are șase felii care se repetă de trei ori, pentru un total de 18 felii. Vom aloca în mod arbitrar un buget zero straturilor cu un singur qubit și RYY, și un buget de .003 fiecărui strat RXX. Vom observa cum specificarea bugetului ca o secvență de lungime 6 face ca bugetul să fie aplicat ciclic tuturor celor 18 felii.
Din nou, reține că feliile sunt retropropagate în ordine inversă (adică începând de la sfârșit). Prin urmare, prima intrare din bugetul nostru ciclic este de fapt folosită pentru ultima felie, a doua intrare pentru felia dinaintea ei și așa mai departe.
# Specify a length-6 per-slice budget.
# This will be cycled over three times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)
op_budget = OperatorBudget(max_qwc_groups=20)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 13 circuit slices.
New observable contains 49 terms and 14 commuting groups.
După cum se vede în imaginile din stânga sus și stânga jos, nu s-a efectuat nicio trunchiere în primele patru felii, deoarece nu li s-a alocat buget de eroare. Feliile cinci și șase au avut o parte din termeni trunchiați pe măsură ce a devenit disponibil bugetul.
După cum se vede în imaginea din stânga sus, o cantitate relativ mare de eroare a fost introdusă după retropropagarea feliei șapte, chiar dacă acelei felii nu i s-a alocat buget. Aceasta deoarece doar aproximativ .002 din bugetul total de .006 alocat feliilor cinci și șase a fost folosit, deci restul a fost transmis feliei șapte și consumat în mare parte, după cum se vede în imaginea din dreapta sus.
Imaginile din stânga sus și dreapta sus arată că mica cantitate de buget rămas este consumată între feliile 8-10, iar buget nou devine disponibil în felia 11, așa cum era de așteptat. Imaginile din stânga sus, dreapta sus și stânga jos demonstrează toate comportamentul ciclic al argumentului max_error_per_slice atunci când lungimea sa este mai mică decât numărul de felii. Acest comportament ciclic ar fi continuat prin toate feliile, dar criteriul de oprire max_qwc_groups a fost atins după retropropagarea a 13 felii, după cum se vede în imaginea din dreapta jos.
Interesant este și că numărul de grupuri Pauli scade de fapt după ce a doua tranșă de buget devine disponibilă în felia 11. Aceasta deoarece existau grupuri cu coeficienți mici care nu au putut fi trunchiate până când a existat mai mult buget după retropropagarea feliei 11, așa că s-au acumulat în observabilă timp de mai multe iterații. Acest caz subliniază și că max_qwc_groups trebuie depășit pentru ca algoritmul să se termine.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Limitează eroarea totală
Pe lângă specificarea bugetului de eroare pe felie, se poate specifica cantitatea maximă de eroare introdusă prin trunchiere. Odată atinsă această limită, nu se mai efectuează trunchiere; totuși, retropropagarea va continua până când observabila devine prea mare și se îndeplinește unul dintre criteriile de oprire.
În continuare rulăm din nou experimentul de mai sus, setând o limită pentru max_error_total astfel încât bugetul de eroare să fie consumat după retropropagarea feliei șapte.
# Specify a length-6 per-slice budget.
# This will be cycled over 3 times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2
truncation_error_budget = setup_budget(
max_error_per_slice=max_error_per_slice, max_error_total=0.006
)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 10 circuit slices.
New observable contains 67 terms and 20 commuting groups.
Așa cum era de așteptat, eroarea noastră de trunchiere atinge plafonul de .006 (graficul din stânga jos). Este notabil că în această rulare nu am putut retropropaga felia 11. Aceasta deoarece nu am avut suficient buget pentru a trunchia termeni, iar numărul de grupuri Pauli comutative a depășit limita de 20, după cum se vede în imaginea din dreapta jos.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Poate fi de dorit să limitezi pur și simplu bugetul total cu max_error_total, fără a specifica max_error_per_slice.
truncation_error_budget = setup_budget(max_error_total=0.018)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)
Rezultatul celulei de mai sus poate fi ușor surprinzător deoarece per_slice_budget este setat la max_error_total. Aceasta indică faptul că întregul buget disponibil va fi consumat lacom (greedy).
Poți gândi astfel: întregul buget este disponibil fiecărei felii (deoarece parcurgem în buclă per_slice_budget). Dar orice buget care a fost deja consumat va fi dedus din bugetul disponibil în acel punct al algoritmului.
op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 9 circuit slices.
New observable contains 25 terms and 9 commuting groups.
Imaginea din dreapta sus arată cum întregul buget de eroare de .018 este pus la dispoziția primei felii. Nu se efectuează nicio trunchiere până la a treia felie, deci bugetul rămas rămâne constant. Bugetul scade monoton, deoarece bugetul complet este pus la dispoziția fiecărei felii retropropagate până când este consumat.
Este notabil că acest experiment a produs cu două felii retropropagate mai puțin decât primul experiment din acest notebook, care este aproape identic. Aceasta demonstrează că, pentru unele probleme, distribuirea uniformă a bugetului poate fi optimă. Pentru alte probleme, permiterea feliilor să consume lacom bugetul complet poate produce performanțe mai bune.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Limitează împreună numărul de felii retropropagate și eroarea totală
Dacă nu vrei să-ți distribui bugetul de eroare pe tot circuitul, dar nici nu vrei să-l consumi lacom, poți specifica numărul de felii pe care intenționezi să le retropropagi (num_slices), împreună cu un buget total de eroare (max_error_total). Aceasta va distribui bugetul de eroare uniform (conform p_norm) între feliile de intrare.
Aici vom limita numărul de felii pe care le-am putea retropropaga la 12 și vom păstra același buget total de eroare.
num_slices = 12
truncation_error_budget = setup_budget(
max_error_total=0.018, num_slices=num_slices, p_norm=1
)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)
Acum vom încerca să retropropagăm cele 12 felii cărora le-am alocat un buget în pasul anterior. Facem aceasta transmițând pur și simplu ultimele 12 felii din circuitul nostru. Cu p_norm=1, fiecare dintre cele 12 felii are un buget disponibil de 0.018 / num_slices = 0.0015.
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices[-num_slices:],
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
Deoarece am transmis un subset al feliilor noastre (în codul slices[-num_slices:]) către backpropagate, trebuie să combinăm feliile rămase după retropropagare cu feliile care nu au fost trimise niciodată la retropropagare (în codul slices[:-num_slices]).
După ce am combinat toate feliile rămase, putem folosi combine_slices pentru a genera QuantumCircuit cu adâncime redusă. Apoi inspectăm câte felii au fost retropropagate față de cât de mare a devenit observabila noastră.
# Recombine the slices remaining after backprop with the rest of the original circuit
reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)
print(f"Backpropagated {num_slices - len(remaining_slices)} circuit slices.")
print(
f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 12 circuit slices.
New observable contains 29 terms and 9 commuting groups.
Graficele arată că am propagat înapoi cu succes toate cele 12 felii, păstrând observabila sub 10 grupuri Pauli care comută. Putem vedea de asemenea că folosirea num_slices împreună cu max_error_total duce la o distribuție a bugetelor pe felii, iar bugetul neutilizat este din nou transmis următoarei felii. Acest lucru este cel mai evident în graficul din dreapta sus, deoarece bugetul este atât consumat, cât și reîntregit pe parcursul propagării înapoi.
Este remarcabil faptul că această metodă de distribuire a erorii a produs cele mai bune rezultate (mai multe felii propagate înapoi), comparativ cu primul experiment din acest notebook și cu exemplul imediat anterior. Toate aceste exemple au alocat un buget de eroare de .018, dar propagarea înapoi a avut rezultate diferite în funcție de modul în care a fost distribuit bugetul.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Lucrul cu mai multe observabile
Metoda qiskit_addon_obp.backpropagate permite transmiterea unei secvențe de observabile. Acest lucru simplifică fluxul de lucru atunci când ai de-a face cu mai multe observabile țintă.
Menționăm explicit acest lucru aici pentru a te învăța cum gestionează strategia de trunchiere un astfel de caz. Pentru acest exemplu, adăugăm o observabilă suplimentară celei pe care am folosit-o până acum:
obs = [SparsePauliOp("IIIIIZIIII"), SparsePauliOp("IIIIIXIIII")]
În final, repetăm primul experiment din acest tutorial, dar cu două observabile în care propagăm circuitul înapoi.
Pentru acest exemplu, acest lucru nu afectează numărul de felii care au putut fi propagate înapoi. Totuși, putem vedea că cele două observabile au dus la numere diferite de termeni Pauli și de grupuri care comută.
truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
obs,
slices,
operator_budget=op_budget,
truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
f"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups."
)
print(
f"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 11 circuit slices.
The new first observable contains 29 terms and 10 commuting groups.
The new second observable contains 23 terms and 8 commuting groups.
Graficele de mai jos ajută la ilustrarea modului în care algoritmul de propagare înapoi gestionează mai multe observabile.
Mai întâi, graficele din stânga sus, dreapta sus și stânga jos arată că bugetul pentru trunchierea termenilor este stabilit individual pentru fiecare observabilă. Cu alte cuvinte, ambele observabile pot trunchia termeni presupunând o eroare de 0.001 per felie propagată înapoi.
Din cauza naturii diferite a observabilelor, rezultă un consum diferit al bugetului. În acest exemplu, ele prezintă o suprapunere mare, ceea ce nu se întâmplă întotdeauna.
Graficul din dreapta jos ilustrează că max_qwc_groups ia în considerare toate observabilele. Asta înseamnă că termenii tuturor observabilelor sunt grupați împreună pentru a ajunge la un singur număr final de grupuri care comută pe qubit, care este comparat cu max_qwc_paulis. Același lucru se face pentru pragul max_paulis (care nu este discutat în acest notebook), care îți permite să stabilești o limită pentru numărul de termeni Pauli din toate observabilele.
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])
