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?

Ladění a trasování

Funkce build() převede anotovaný obvod na dvojici šablonového obvodu a samplexu. Ve složitém obvodu může být těžké poznat, který box v původním obvodu odpovídá kterým bariérám v šabloně nebo kterým uzlům v DAG samplexu.

Samplomatic poskytuje dva doplňující se nástroje pro trasování:

  • Popisky bariér: Každá bariéra v šablonovém obvodu nese popisek, který kóduje její původ. Jsou vždy přítomny bez ohledu na to, zda je nastaveno debug=True.

  • Trasovací informace na uzlech samplexu: Když je build() zavoláno s debug=True, každý uzel samplexu nese objekt TraceInfo, který ho propojuje s boxem nebo boxy, které ho vytvořily.

Oba nástroje jsou nejužitečnější, když boxy nesou anotaci Tag, která k boxu připojí řetězec ref. Alternativně se připojují i reference InjectNoise.

Označení boxů​

Anotace Tag připojí k boxu řetězec ref. Tento ref se pak objeví v popiscích bariér a v trasovacích informacích na uzlech samplexu.

Následující ukázkový obvod má tři boxy: dva označené boxy CX na disjunktních dvojicích qubitů a neoznačený box měření s pravým dressingem. Druhý box CX nese také anotaci InjectNoise — jeho reference tag i reference šumu ref se objeví v popiscích bariér. Oba boxy CX pokrývají různé qubity, což optimalizátoru samplexu umožňuje sloučit jejich propagační uzly do sdílených uzlů. To je ukázáno v sekci o trasovacích informacích.

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

Popisky bariér v šablonovém obvodu​

Zavolání build() na obvodu výše vytvoří šablonu, jejíž bariéry nesou identifikační popisky. Každý popisek má tvar {side}{scope}@{key=value&...}:

  • Strana (Side): L = hranice levého dressingu, M = hranice vnitřního obsahu boxu, R = hranice pravého dressingu.

  • Rozsah (Scope): Celočíselný index (nebo seznam oddělený podtržítky pro vnořené boxy), který odlišuje více boxů na téže úrovni vnoření.

  • Anotace: Seznam dvojic key=value s prefixem @ a oddělovačem &, odvozený z anotací boxu. Klíč tag pochází z anotace Tag a inject_noise z anotace InjectNoise.

Bariéry z neoznačených boxů nesou jen stranu a rozsah (například L2), bez přípony @.

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

../_images/e533d70a29504005a636aa27306d3ce3b1d2488358ac6a53f0674f2934698ab8.png

Popisky bariér lze také získat programově:

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']

Trasovací informace na uzlech samplexu​

Předání debug=True do build() připojí trasovací informace ke každému uzlu samplexu. Atribut trace_info každého uzlu je objekt TraceInfo, jehož slovník trace_refs mapuje klíče anotací (například "tag" a "inject_noise") na množiny řetězců ref. Uzly bez odpovídající anotace boxu mají trace_info=None.

Když je draw() zavoláno na samplexu sestaveném v režimu ladění, najetí na libovolný uzel v interaktivním grafu zobrazí jeho trace_refs v tooltipu.

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

Trasovací informace lze také zkoumat programově. Všimni si, že některé uzly nesou reference z 'cx_ab' i 'cx_cd'. Optimalizátor samplexu sloučil jejich paralelní propagační uzly, protože oba boxy pokrývají disjunktní qubity a sdílejí společného předchůdce z emise boxu měření s pravým dressingem.

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

Chceš-li najít všechny uzly pocházející z konkrétního boxu, filtruj podle klíče "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 samplexu​

Samplex vrácený funkcí build() je orientovaný acyklický graf (DAG), kde hrany označují závislost registrů. To znamená, že hrana z uzlu A do uzlu B značí, že B musí počkat, až A zapůsobí na sdílené virtuální registry, než smí B působit sám. Nejde o časové uspořádání jako u DAG kvantového obvodu. Graf spíše teče od uzlů odpovědných za generování randomizací k uzlům odpovědným za jejich syntézu na výstupy.

Existují tři typy uzlů, z nichž každý má v interaktivním grafu odlišný vizuální styl:

TvarBarvaTypRole
HvězdaČervenáSamplingNodeVytváří nové virtuální registry z rozdělení nebo vstupu
KruhZelenáEvaluationNodeTransformuje, kombinuje nebo propaguje virtuální registry
MotýlekModrá / FialováCollectionNodeČte registry a zapisuje do výstupů sample()

Provádění probíhá ve třech fázích:

  1. Nejprve se spustí všechny uzly vzorkování (paralelně).

  2. Poté se spustí uzly vyhodnocení v topologickém pořadí (paralelně v rámci každé generace).

  3. Nakonec se spustí uzly sběru (paralelně).

Rozložení graphviz to odráží. Uzly vzorkování jsou nahoře, uzly sběru dole.

Tooltipy při najetí myší​

Každý uzel v interaktivní vizualizaci zobrazí po najetí myší tooltip. Tooltip obsahuje následující informace:

  • Název třídy uzlu a jeho celočíselný index v grafu.

  • Manifesty registrů, tedy které registry uzel vytváří, čte, zapisuje do nich a odstraňuje. Říkají ti, jak data proudí mezi uzly.

  • Podrobnosti specifické pro uzel, včetně typu rozdělení u uzlů vzorkování, operandu u uzlů násobení, indexů parametrů šablony u uzlů sběru a tak dále.

  • Trace refs (pouze s debug=True), tedy klíče anotací a řetězce ref, které uzel propojují s původními boxy.

Kliknutí na graf a následné najíždění na jednotlivé uzly je nejrychlejší způsob, jak pochopit, co daný uzel dělá, aniž bys četl zdrojový kód.

Souhrn samplexu​

Před prozkoumáním vizualizace poskytne print(samplex) rychlý textový souhrn počtu uzlů, požadovaných vstupů a slíbených výstupů.

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.

Zkoumání registrů​

Předání keep_registers=True do sample() zachová mezilehlé objekty VirtualRegister, které jsou živé na konci vzorkování, a uloží je do outputs.metadata["registers"]. Každý registr je 2D pole (tvar (num_subsystems, num_randomizations), s možnými koncovými rozměry tvaru hradla) prvků virtuální grupy.

To je užitečné pro ověření, že virtuální hradla byla správně zkombinována, nebo pro zkoumání surových Pauliových či unitárních vzorků dříve, než jsou syntetizovány do úhlů rotace.

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)

Můžeš si prohlédnout obsah koncového stavu konkrétního registru. V následujícím příkladu má registr typ PauliRegister. Podrobnosti o formátu uložení najdeš v API documentation(/apidocs/index)__.

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)

Jak se uzly samplexu mapují na parametry šablony​

Pole outputs["parameter_values"] vrácené funkcí sample() má tvar (num_randomizations, N), kde N odpovídá len(template.parameters). Každý sloupec odpovídá jednomu parametru v šablonovém obvodu — i-tý sloupec vyplňuje template.parameters[i].

Uzly sběru CollectTemplateValues jsou spojnicí mezi virtuálními registry a parametry šablony. Každý takový uzel drží informace o indexech, které zaznamenávají, do kterých sloupců výstupního pole zapisuje. Díky tomu lze vysledovat, které podsystémy virtuálních hradel řídí které parametry šablony.

V průvodci {Samplex inputs and outputs}samplex-io se dozvíš, jak navázat vzorkované hodnoty parametrů na šablonový obvod a spouštět experimenty.

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]]

Automatické označování​

Při použití generate_boxing_pass_manager() parametr add_tags automaticky přidá anotace Tag ke všem boxům. K dispozici jsou tři režimy:

  • "unique_instance": Přiřadí boxům v pořadí obvodu sekvenční ref (t0, t1 a tak dále). Každý box dostane odlišný ref bez ohledu na svou strukturu.

  • "unique_box": Spočítá strukturální hash obsahu každého boxu a všem strukturně ekvivalentním boxům přiřadí stejný ref. To je užitečné pro seskupování boxů podle typu místo podle pozice.

  • "noise_ref": Zkopíruje ref z anotace InjectNoise každého boxu a označí pouze boxy, které ji mají. To je užitečné, pokud smysluplné noise refs již existují.

Následující příklad použije "unique_instance" a "unique_box" na obvod, jehož dva boxy CX jsou strukturně ekvivalentní. S "unique_instance" dostane každý odlišný ref, zatímco s "unique_box" sdílejí jeden.

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