Sari la conținutul principal
Acest proiect este în versiune beta. Ai găsit o eroare? Deschide un issue pe GitHub. Ai o întrebare sau o idee? Întreabă în Discussions — ne-ar plăcea să aflăm părerea ta!
Cum este calitatea traducerii pe această pagină?

Depanare și urmărire

Funcția build() transformă un circuit adnotat într-o pereche formată dintr-un circuit șablon și un samplex. Într-un circuit complex poate fi greu de spus ce cutie din circuitul original corespunde căror bariere din șablon sau căror noduri din DAG-ul samplex.

Samplomatic oferă două instrumente complementare de urmărire:

  • Etichete de barieră: Fiecare barieră din circuitul șablon poartă o etichetă care codifică originea ei. Acestea sunt întotdeauna prezente, indiferent dacă debug=True este setat sau nu.

  • Informații de urmărire pe nodurile samplex: Când build() este apelată cu debug=True, fiecare nod samplex poartă un obiect TraceInfo care îl leagă înapoi de cutia sau cutiile care l-au produs.

Ambele instrumente sunt cele mai utile când cutiile poartă o adnotare Tag, care atașează cutiei un șir ref. Alternativ, sunt atașate și referințele InjectNoise.

Etichetează cutiile​

O adnotare Tag atașează unei cutii un șir ref. ref apare apoi în etichetele de barieră și în informațiile de urmărire de pe nodurile samplex.

Circuitul de exemplu următor are trei cutii: două cutii CX etichetate pe perechi de qubiți disjuncte și o cutie de măsurare neetichetată cu îmbrăcare la dreapta. A doua cutie CX poartă și o adnotare InjectNoise — atât referința ei tag, cât și referința ei de zgomot ref vor apărea în etichetele de barieră. Cele două cutii CX acoperă qubiți diferiți, ceea ce permite optimizatorului samplex să le îmbine nodurile de propagare în noduri comune. Acest lucru este prezentat în secțiunea despre informațiile de urmărire.

from qiskit.circuit import QuantumCircuit

from samplomatic import InjectNoise, Tag, Twirl, build

circuit = QuantumCircuit(4, 4)

with circuit.box([Twirl(), Tag("cx_ab")]):
circuit.cx(0, 1)

with circuit.box([Twirl(), InjectNoise("cx_noise"), Tag("cx_cd")]):
circuit.cx(2, 3)

with circuit.box([Twirl(), Tag("meas_box")]):
circuit.measure(range(4), range(4))

circuit.draw("mpl")

../_images/f6b6fe7b60a39ba897814008443fa79b66213005462ab09f49ffc89aab1f3668.png

Etichete de barieră în circuitul șablon​

Apelarea build() pe circuitul de mai sus produce un șablon ale cărui bariere poartă etichete de identificare. Fiecare etichetă are forma {side}{scope}@{key=value&...}:

  • Side: L = limita de îmbrăcare la stânga, M = limita conținutului interior al cutiei, R = limita de îmbrăcare la dreapta.

  • Scope: Un index întreg (sau o listă separată prin liniuțe de subliniere pentru cutii imbricate) care distinge mai multe cutii de la același nivel de imbricare.

  • Adnotări: O listă de perechi key=value, prefixată cu @ și separată prin &, derivată din adnotările cutiei. Cheia tag provine dintr-o adnotare Tag, iar inject_noise provine dintr-o adnotare InjectNoise.

Barierele din cutiile neetichetate poartă doar side și scope (de exemplu, L2), fără sufixul @.

template, samplex = build(circuit)
template.draw("mpl", fold=100)

../_images/e533d70a29504005a636aa27306d3ce3b1d2488358ac6a53f0674f2934698ab8.png

Etichetele de barieră pot fi extrase și programatic:

barrier_labels = [instr.operation.label for instr in template if instr.operation.name == "barrier"]
barrier_labels
['L0@tag=cx_ab',
'M0@tag=cx_ab',
'R0@tag=cx_ab',
'L1@inject_noise=cx_noise&tag=cx_cd',
'M1@inject_noise=cx_noise&tag=cx_cd',
'R1@inject_noise=cx_noise&tag=cx_cd',
'L2@tag=meas_box',
'M2@tag=meas_box',
'R2@tag=meas_box']

Informații de urmărire pe nodurile samplex​

Transmiterea debug=True către build() atașează informații de urmărire fiecărui nod samplex. Atributul trace_info al fiecărui nod este un obiect TraceInfo al cărui dicționar trace_refs asociază cheile de adnotare (de exemplu, "tag" și "inject_noise") cu mulțimi de șiruri ref. Nodurile fără o adnotare de cutie corespunzătoare au trace_info=None.

Când draw() este apelată pe un samplex construit în mod debug, trecerea cursorului peste orice nod din graful interactiv dezvăluie trace_refs în tooltip.

template, samplex = build(circuit, debug=True)
samplex.draw()

Informațiile de urmărire pot fi inspectate și programatic. Observă că unele noduri poartă referințe atât de la 'cx_ab', cât și de la 'cx_cd'. Optimizatorul samplex le-a îmbinat nodurile de propagare paralele deoarece cele două cutii acoperă qubiți disjuncți și au un predecesor comun din emisia cutiei de măsurare cu îmbrăcare la dreapta.

for node in samplex.graph.nodes():
if node.trace_info is not None:
tags = node.trace_info.trace_refs.get("tag", set())
merged = " ← merged" if len(tags) > 1 else ""
print(f"{type(node).__name__:40s} tags={tags}{merged}")
TwirlSamplingNode tags={'meas_box'}
InjectNoiseNode tags={'cx_cd'}
CollectZ2ToOutputNode tags={'cx_cd'}
TwirlSamplingNode tags={'cx_cd'}
CombineRegistersNode tags={'cx_cd'}
CollectTemplateValues tags={'cx_cd'}
TwirlSamplingNode tags={'cx_ab'}
CombineRegistersNode tags={'cx_ab', 'cx_cd'} ← merged
CollectTemplateValues tags={'cx_ab', 'cx_cd'} ← merged

Pentru a găsi toate nodurile care provin dintr-o anumită cutie, filtrează după cheia "tag":

tag_ref = "cx_ab"
matching_nodes = [
node
for node in samplex.graph.nodes()
if node.trace_info is not None and tag_ref in node.trace_info.trace_refs.get("tag", set())
]

print(f"Nodes originating from box '{tag_ref}':")
for node in matching_nodes:
print(f" {type(node).__name__}")
Nodes originating from box 'cx_ab':
TwirlSamplingNode
CombineRegistersNode
CollectTemplateValues

DAG-ul samplex​

Samplex-ul returnat de build() este un graf aciclic orientat (DAG) în care muchiile denotă dependența de registre. Adică, o muchie de la nodul A la nodul B înseamnă că B trebuie să aștepte ca A să fi acționat asupra registrelor virtuale comune înainte ca B să aibă voie să acționeze. Aceasta nu este o ordonare temporală precum DAG-ul unui circuit cuantic. În schimb, graful curge de la nodurile responsabile de generarea randomizărilor către nodurile responsabile de sintetizarea lor ca ieșiri.

Există trei tipuri de noduri, fiecare cu un stil vizual distinct în graficul interactiv:

FormăCuloareTipRol
SteaRoșuSamplingNodeInstanțiază registre virtuale noi dintr-o distribuție sau dintr-o intrare
CercVerdeEvaluationNodeTransformă, combină sau propagă registre virtuale
PapionAlbastru / MovCollectionNodeCitește registre și scrie în ieșirile sample()

Execuția are loc în trei faze:

  1. Toate nodurile de eșantionare rulează primele (în paralel).

  2. Nodurile de evaluare rulează apoi, în ordine topologică (în paralel în cadrul fiecărei generații).

  3. Nodurile de colectare rulează ultimele (în paralel).

Aspectul graphviz reflectă acest lucru. Nodurile de eșantionare apar în partea de sus, nodurile de colectare în partea de jos.

Tooltip-uri la trecerea cursorului​

Fiecare nod din vizualizarea interactivă afișează un tooltip când treci cursorul peste el. Tooltip-ul conține următoarele informații:

  • Numele clasei nodului și indexul său întreg din graf.

  • Manifestele de registre, adică ce registre instanțiază, citește, scrie și elimină nodul. Acestea îți arată cum circulă datele între noduri.

  • Detalii specifice nodului, inclusiv tipul de distribuție pentru nodurile de eșantionare, operandul pentru nodurile de înmulțire, indicii parametrilor șablonului pentru nodurile de colectare și așa mai departe.

  • Trace refs (doar când debug=True), adică cheile de adnotare și șirurile ref care leagă nodul de cutiile din care provine.

Să dai clic pe grafic și apoi să treci cursorul peste noduri individuale este cea mai rapidă metodă de a înțelege ce face un anumit nod fără a citi codul sursă.

Rezumatul samplex​

Înainte de a examina vizualizarea, print(samplex) oferă un rezumat text rapid al numărului de noduri, al intrărilor necesare și al ieșirilor promise.

template, samplex = build(circuit)
print(samplex)
Samplex(<17 nodes>)
Inputs:
- 'pauli_lindblad_maps.cx_noise' <PauliLindbladMap>: A PauliLindblad map acting on 2
qubits, with 'num_terms_cx_noise' terms.

Outputs:
* 'measurement_flips.c' <bool['num_randomizations', 1, 4]>: Bit-flip corrections for
measurement twirling.
* 'parameter_values' <float32['num_randomizations', 24]>: Parameter values valid for an
associated template circuit.
* 'pauli_signs' <bool['num_randomizations', 1]>: Signs from sampled Pauli Lindblad
maps, where boolean values represent the parity of the number of non-trivial factors in the
sampled error that arise from negative rates. In other words, in order to implement basic
PEC, the sign used to correct expectation values should be ``(-1)**bool_value``. The order
matches the iteration order of boxes in the original circuit with noise injection
annotations.

Inspectează registrele​

Transmiterea keep_registers=True către sample() păstrează obiectele intermediare VirtualRegister care sunt active la finalul eșantionării și le stochează în outputs.metadata["registers"]. Fiecare registru este un tablou 2D (formă (num_subsystems, num_randomizations), cu eventuale dimensiuni finale pentru forma porții) de elemente de grup virtuale.

Acest lucru este util pentru a verifica dacă porțile virtuale au fost combinate corect sau pentru a inspecta eșantioanele Pauli sau unitare brute înainte ca acestea să fie sintetizate în unghiuri de rotație.

from qiskit.quantum_info import PauliLindbladMap

outputs = samplex.sample(
{"pauli_lindblad_maps.cx_noise": PauliLindbladMap.identity(2)},
num_randomizations=3,
keep_registers=True,
)
for name, reg in outputs.metadata["registers"].items():
print(f"{name}: type={reg.TYPE.value}, shape={reg.virtual_gates.shape}")
lhs_0: type=pauli, shape=(4, 3)
rhs_0: type=pauli, shape=(4, 3)
lhs_3: type=pauli, shape=(2, 3)
rhs_3: type=pauli, shape=(2, 3)
inject_noise_1: type=pauli, shape=(2, 3)
sign_1: type=z2, shape=(1, 3)
lhs_7: type=pauli, shape=(2, 3)
rhs_7: type=pauli, shape=(2, 3)
meas_prop_6: type=pauli, shape=(4, 3)
collect_10: type=u2, shape=(2, 3, 2, 2)
meas_prop_z2a_6: type=z2, shape=(4, 3)
collect_9: type=u2, shape=(4, 3, 2, 2)
collect_5: type=u2, shape=(2, 3, 2, 2)

Poți vizualiza conținutul stării finale a unui anumit registru. În exemplul următor, registrul are tipul PauliRegister. Consultă API documentation(/apidocs/index)__ pentru detalii despre formatul de stocare.

print("lhs_0:", outputs.metadata["registers"]["lhs_0"])
outputs.metadata["registers"]["lhs_0"].virtual_gates
lhs_0: PauliRegister(<4, 3>)
array([[0, 0, 1],
[3, 1, 1],
[3, 0, 0],
[1, 0, 0]], dtype=uint8)

Cum se asociază nodurile samplex cu parametrii șablonului​

Tabloul outputs["parameter_values"] returnat de sample() are forma (num_randomizations, N), unde N este egal cu len(template.parameters). Fiecare coloană corespunde unui parametru din circuitul șablon — a i-a coloană completează template.parameters[i].

Nodurile de colectare CollectTemplateValues sunt legătura dintre registrele virtuale și parametrii șablonului. Fiecare astfel de nod păstrează informații de index care înregistrează în ce coloane ale tabloului de ieșire scrie. Astfel se poate urmări ce subsisteme de porți virtuale determină ce parametri ai șablonului.

Consultă ghidul {Samplex inputs and outputs}samplex-io pentru a afla cum să legi valorile de parametri eșantionate de circuitul șablon și să rulezi experimente.

from samplomatic.samplex.nodes import CollectTemplateValues

template, samplex = build(circuit, debug=True)
for node in samplex.graph.nodes():
if isinstance(node, CollectTemplateValues):
tags = node.trace_info.trace_refs.get("tag", set()) if node.trace_info else set()
print(f"tags={tags} → template param indices: {node.template_idxs.tolist()}")
tags={'cx_cd'} → template param indices: [[6, 7, 8], [9, 10, 11]]
tags={'cx_ab', 'cx_cd'} → template param indices: [[12, 13, 14], [15, 16, 17], [18, 19, 20], [21, 22, 23]]
tags=set() → template param indices: [[0, 1, 2], [3, 4, 5]]

Etichetare automată​

Când folosești generate_boxing_pass_manager(), parametrul add_tags adaugă automat adnotări Tag tuturor cutiilor. Sunt disponibile trei moduri:

  • "unique_instance": Atribuie ref secvențiale (t0, t1 și așa mai departe) casetelor, în ordinea circuitului. Fiecare casetă primește un ref distinct, indiferent de structura ei.

  • "unique_box": Calculează un hash structural al conținutului fiecărei casete și atribuie același ref tuturor casetelor echivalente structural. Este util pentru gruparea casetelor după tip, nu după poziție.

  • "noise_ref": Copiază ref din adnotarea InjectNoise a fiecărei casete și etichetează doar casetele care au una. Este util când există deja referințe de zgomot semnificative.

Exemplul următor aplică "unique_instance" și "unique_box" unui circuit ale cărui două boxuri CX sunt echivalente structural. Cu "unique_instance", fiecare primește un ref distinct, în timp ce cu "unique_box" ele partajează unul singur.

from samplomatic.transpiler import generate_boxing_pass_manager

base_circuit = QuantumCircuit(3)
base_circuit.cx(0, 1)
base_circuit.cx(1, 2)
base_circuit.measure_all()

# unique_instance: every box gets a distinct tag ref
pm = generate_boxing_pass_manager(add_tags="unique_instance")
boxed = pm.run(base_circuit)
template, _ = build(boxed)

print("unique_instance barrier labels:")
for instr in template:
if instr.operation.name == "barrier" and instr.operation.label:
print(f" {instr.operation.label}")
unique_instance barrier labels:
L0@tag=t0
M0@tag=t0
R0@tag=t0
L1@tag=t1
M1@tag=t1
R1@tag=t1
L2@tag=t2
M2@tag=t2
R2@tag=t2