Penyahpepijatan dan penjejakan
Fungsi build() mengubah litar beranotasi menjadi pasangan litar templat dan samplex. Dalam litar yang kompleks, sukar untuk mengetahui kotak mana dalam litar asal yang sepadan dengan penghadang mana dalam templat, atau nod mana dalam DAG samplex.
Samplomatic menyediakan dua alat penjejakan yang saling melengkapi:
-
Label penghadang: Setiap penghadang dalam litar templat membawa label yang mengekodkan asalnya. Label ini sentiasa ada sama ada
debug=Trueditetapkan atau tidak. -
Maklumat jejak pada nod samplex: Apabila
build()dipanggil dengandebug=True, setiap nod samplex membawa objekTraceInfoyang memautkannya kembali kepada kotak atau kotak-kotak yang menghasilkannya.
Kedua-dua alat paling berguna apabila kotak membawa anotasi Tag, yang melampirkan rentetan ref pada kotak. Sebagai alternatif, rujukan InjectNoise juga dilampirkan.
Kotak tag
Anotasi Tag melampirkan rentetan ref pada kotak. ref itu kemudian muncul dalam label penghadang dan dalam maklumat jejak pada nod samplex.
Contoh litar berikut mempunyai tiga kotak: dua kotak CX bertag pada pasangan qubit yang tidak bertindih, dan satu kotak pengukuran right-dressed tanpa tag. Kotak CX kedua turut membawa anotasi InjectNoise — rujukan tag dan ref hingarnya akan muncul dalam label penghadang. Kedua-dua kotak CX meliputi qubit yang berbeza, yang membolehkan pengoptimum samplex menggabungkan nod perambatan mereka menjadi nod bersama. Ini ditunjukkan dalam bahagian maklumat surih.
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")
Label penghadang dalam litar templat
Memanggil build() pada litar di atas menghasilkan templat yang penghadangnya membawa label pengenalan. Setiap label berbentuk {side}{scope}@{key=value&...}:
-
Sisi:
L= sempadan pemakaian-kiri,M= sempadan kandungan dalam kotak,R= sempadan pemakaian-kanan. -
Skop: Indeks integer (atau senarai dipisahkan garis bawah untuk kotak bersarang) yang membezakan beberapa kotak pada aras sarang yang sama.
-
Anotasi: Senarai pasangan
key=valueberawalan@dan dipisahkan&yang diperoleh daripada anotasi kotak. Kuncitagdatang daripada anotasiTagdaninject_noisedatang daripada anotasiInjectNoise.
Penghadang daripada kotak tidak bertag hanya membawa sisi dan skop (contohnya, L2), tanpa akhiran @.
template, samplex = build(circuit)
template.draw("mpl", fold=100)
Label penghadang juga boleh diekstrak secara programatik:
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']
Maklumat jejak pada nod samplex
Menghantar debug=True kepada build() melampirkan maklumat jejak pada setiap nod samplex. Atribut trace_info setiap nod ialah objek TraceInfo yang kamus trace_refsnya memetakan kunci anotasi (contohnya, "tag" dan "inject_noise") kepada set rentetan ref. Nod tanpa anotasi kotak yang sepadan mempunyai trace_info=None.
Apabila draw() dipanggil pada samplex yang dibina dalam mod nyahpepijat, menuding ke atas mana-mana nod dalam graf interaktif akan mendedahkan trace_refsnya dalam tip alat tuding.
template, samplex = build(circuit, debug=True)
samplex.draw()
Maklumat jejak juga boleh diperiksa secara programatik. Perhatikan bahawa sesetengah nod membawa rujukan daripada kedua-dua 'cx_ab' dan 'cx_cd'. Pengoptimum samplex menggabungkan nod perambatan selari mereka kerana kedua-dua kotak meliputi qubit tak bertindih dan berkongsi pendahulu sepunya daripada pemancaran kotak pengukuran berpakaian-kanan.
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
Untuk mencari semua nod yang berasal daripada kotak tertentu, tapis mengikut kunci "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 samplex
Samplex yang dikembalikan oleh build() ialah graf berarah tanpa kitaran (DAG) di mana sisi menandakan kebergantungan daftar. Maksudnya, sisi daripada nod A ke nod B bermakna B mesti menunggu A bertindak ke atas daftar maya yang dikongsi sebelum B dibenarkan bertindak. Ini bukan tertib temporal seperti DAG litar kuantum. Sebaliknya, graf mengalir daripada nod yang bertanggungjawab menjana perawakan kepada nod yang bertanggungjawab mensintesisnya sebagai output.
Terdapat tiga jenis nod, masing-masing dengan gaya visual tersendiri dalam plot interaktif:
| Bentuk | Warna | Jenis | Peranan |
|---|---|---|---|
| Bintang | Merah | SamplingNode | Mewujudkan daftar maya baharu daripada taburan atau input |
| Bulatan | Hijau | EvaluationNode | Mengubah, menggabungkan, atau merambatkan daftar maya |
| Reben | Biru / Ungu | CollectionNode | Membaca daftar dan menulis ke output sample() |
Pelaksanaan berlangsung dalam tiga fasa:
-
Semua nod persampelan dijalankan dahulu (secara selari).
-
Nod penilaian dijalankan seterusnya, mengikut tertib topologi (selari dalam setiap generasi).
-
Nod koleksi dijalankan terakhir (secara selari).
Susun atur graphviz mencerminkan hal ini. Nod persampelan muncul di bahagian atas, nod koleksi di bahagian bawah.
Tip alat tuding
Setiap nod dalam visualisasi interaktif menunjukkan tip alat apabila anda menuding di atasnya. Tip alat mengandungi maklumat berikut:
-
Nama kelas nod dan indeks grafnya yang berupa integer.
-
Manifes daftar, iaitu daftar yang diwujudkan, dibaca, ditulis, dan dibuang oleh nod. Ini memberitahu anda bagaimana data mengalir antara nod.
-
Butiran khusus nod, termasuk jenis taburan bagi nod persampelan, operan bagi nod pendaraban, indeks parameter templat bagi nod koleksi, dan seterusnya.
-
Trace refs (hanya apabila
debug=True), iaitu kunci anotasi dan rentetan ref yang memautkan nod kembali kepada kotak asalnya.
Mengklik plot dan kemudian menuding ke atas nod individu ialah cara terpantas untuk memahami fungsi sesuatu nod tanpa membaca kod sumber.
Ringkasan samplex
Sebelum memeriksa visualisasi, print(samplex) memberikan ringkasan teks pantas tentang bilangan nod, input yang diperlukan, dan output yang dijanjikan.
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.
Periksa daftar
Menghantar keep_registers=True kepada sample() mengekalkan objek VirtualRegister perantaraan yang masih hidup pada akhir persampelan, dan menyimpannya dalam outputs.metadata["registers"]. Setiap daftar ialah tatasusunan 2D (bentuk (num_subsystems, num_randomizations), dengan kemungkinan dimensi bentuk-gate di hujung) bagi unsur kumpulan maya.
Ini berguna untuk mengesahkan bahawa gate maya telah digabungkan dengan betul, atau untuk memeriksa sampel Pauli atau unitari mentah sebelum disintesis menjadi sudut putaran.
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)
Anda boleh melihat kandungan keadaan akhir bagi daftar tertentu. Dalam contoh berikut, daftar itu berjenis PauliRegister. Lihat API documentation(/apidocs/index)__ untuk butiran tentang format storan.
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)
Bagaimana nod samplex dipetakan kepada parameter templat
Tatasusunan outputs["parameter_values"] yang dikembalikan oleh sample() mempunyai bentuk (num_randomizations, N), dengan N sepadan dengan len(template.parameters). Setiap lajur sepadan dengan satu parameter dalam litar templat — lajur ke-i mengisi template.parameters[i].
Nod koleksi CollectTemplateValues ialah pautan antara daftar maya dan parameter templat. Setiap nod sedemikian memegang maklumat indeks yang merekodkan lajur tatasusunan output yang ditulisnya. Ini memungkinkan penjejakan subsistem gate maya mana yang memacu parameter templat mana.
Lihat panduan {Samplex inputs and outputs}samplex-io untuk mempelajari cara mengikat nilai parameter yang disampel pada litar templat dan menjalankan eksperimen.
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]]
Penandaan automatik
Apabila menggunakan generate_boxing_pass_manager(), parameter add_tags secara automatik menambah anotasi Tag pada semua kotak. Tiga mod tersedia:
-
"unique_instance": Menetapkanrefberurutan (t0,t1, dan seterusnya) kepada box mengikut susunan litar. Setiap box mendapatrefyang berbeza tanpa mengira strukturnya. -
"unique_box": Mengira hash struktur bagi kandungan setiap box dan menetapkanrefyang sama kepada semua box yang setara dari segi struktur. Ini berguna untuk mengumpulkan box mengikut jenis dan bukannya kedudukan. -
"noise_ref": Menyalinrefdaripada anotasiInjectNoisesetiap box dan hanya menandakan box yang memilikinya. Ini berguna apabila noise ref yang bermakna sudah wujud.
Contoh berikut menggunakan "unique_instance" dan "unique_box" pada sebuah litar yang mempunyai dua kotak CX yang setara dari segi struktur. Dengan "unique_instance", setiap kotak mendapat ref yang berbeza, manakala dengan "unique_box", kedua-duanya berkongsi satu.
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
