デバッグおよびトレース
この関 build() 数は、アノテーションが付けられた回路を、テンプレート回路とサンプレックス・ペアに変換します。 複雑な回路では、元の回路のどのボックスがテンプレートのどの障壁に対応しているのか、あるいはサンプレックスDAGのどのノードに対応しているのかを見分けるのが難しい場合があります。
Samplomaticでは、互いに補完し合う2つのトレースツールを提供しています:
- バリアのラベル :テンプレート回路内のすべてのバリアには、その起源を符号化したラベルが付いています。 これらは、が設定
debug=Trueされているかどうかにかかわらず、常に存在します。 - サンプレックスノードのトレース情報 :がで呼び出
build()されるとdebug=True、各サンプレックスノードは、それを作成したボックスへのリンクとなるオブジェクトTraceInfoを保持します。
どちらのツールも、ボックスに Tag 注釈が付けられている場合に最も役立ちます。注釈とは、ボックスに文字 ref 列を関連付けるものです。 あるいは、参考 InjectNoise 資料も添付されています。
タグボックス
アノテーション Tag は、ボックスに文字 ref 列を関連付けます。 この は ref 、バリアラベルやサンプレックスノードのトレース情報に表示されます。
次の回路例には、3つのボックスがあります。2つのタグ付きCXボックスは互いに交わらない量子ビットのペアに接続されており、もう1つはタグなしの右向き測定ボックスです。 2番目のCXボックスにも InjectNoise 注釈が付いています。このボックスの「ref tag 」と「noise」の両方が、バリアラベルに表示 ref されます。 2つのCXボックスは異なる量子ビットをカバーしているため、サンプレックス最適化アルゴリズムはそれらの伝播ノードを共有ノードに統合することができます。 これはトレース情報のセクションに表示されます。
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")
テンプレート回路内のバリアラベル
上記の回路を呼び出す build() と、バリアに識別用のラベルが付いたテンプレートが生成されます。 各ラベルは次の形式になっています {side}{scope}@{key=value&...}:
- サイド :
L= 左揃えの境界、M= 内側のボックスのコンテンツ境界、R= 右揃えの境界。 - スコープ :同じネストレベルにある複数のボックスを区別するための整数インデックス(ネストされたボックスの場合はアンダースコアで区切られたリスト)。
- 注釈 :ボックスの注釈から導出された、-で
@始まる、-で&区切られたペアkey=valueのリスト。 キーtagは アノテーションTagから取得され、 は アノテーションInjectNoiseから取得inject_noiseされます。
タグのないボックスからのバリアは、サイドとスコープ(例: L2)のみを持ち、接尾 @ 辞は付きません。
template, samplex = build(circuit)
template.draw("mpl", fold=100)
バリアラベルは、プログラムによって抽出することも可能です:
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']
samplexノードに関するトレース情報
passing を to debug=True に指定すると、すべての samplex ノードにトレース情報が付加 build() されます。 各ノードの属性 trace_info は、注釈キー(例えば、 "tag" および "inject_noise")を ref 文字列の集合にマッピングする辞書 trace_refs を持つ TraceInfo オブジェクトです。 対応するボックスの注釈がないノードには、. が付いています trace_info=None。
デバッグビルド版のsamplexで が draw() 呼び出されると、対話型グラフ上の任意のノードにカーソルを合わせると、そのノードの がツールチップ trace_refs 内に表示されます。
template, samplex = build(circuit, debug=True)
samplex.draw()トレース情報は、プログラムから確認することもできます。 一部のノードには、 'cx_ab' と双方からの参照が含まれていることに注意してください 'cx_cd'。 Samplexオプティマイザーは、2つのボックスが互いに交わらない量子ビットをカバーしており、かつ「右向き」測定ボックスの放出から共通の先行量子を持つため、それらの並列伝播ノードを統合しました。
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
特定のボックスを起点とするすべてのノードを検索するには、次のキー "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
Samplex DAG
によって返されるサンプレックスは、 辺がレジスタ依存関係を表す有向非循環グラフ(DAG)である build() 。 つまり、ノードAからノードBへのエッジがあるということは、Bが動作を許可される前に、BはAが共有仮想レジスタに対して処理を完了するのを待たなければならないことを意味する。 これは、量子回路のDAGのような時間的な順序付けではありません。 その代わりに、このグラフは、ランダム化を生成するノードから、それらを出力として合成するノードへとつながっています。
ノードには3つのタイプがあり、インタラクティブなプロット上ではそれぞれ異なる視覚的スタイルで表示されます:
形 | カラー | タイプ | 役割 |
|---|---|---|---|
| 星 | 赤色 | SamplingNode | 配布または入力から新しい仮想レジスタをインスタンス化します |
| 円 | 緑色 | EvaluationNode | 仮想レジスタを変換、結合、または伝播する |
| 蝶ネクタイ | 青/紫 | CollectionNode | レジスタを読み取り、出力 sample() に書き込みを行う |
実行は3つの段階に分けて行われます:
- すべてのサンプリングノードが最初に(並列で)実行されます。
- 次に、評価ノードがトポロジカル順に実行されます(各世代内では並列に実行されます)。
- コレクションノードは最後に(並列で)実行されます。
このことがGraphvizのレイアウトにも反映されています。 サンプリングノードは上部に、収集ノードは下部に表示されます。
ホバー時のツールチップ
インタラクティブな可視化画面の各ノードは、マウスをその上に合わせるとツールチップが表示されます。 ツールチップには、以下の情報が含まれています:
- ノードのクラス名とその整数型のグラフインデックス。
- マニフェストを登録します。これにより、ノードがインスタンス化、読み取り、書き込み、および削除を行う対象が登録されます。 これらは、ノード間でデータがどのように流れるかを示しています。
- サンプリングノードの分布タイプ、乗算ノードのオペランド、コレクションノードのテンプレートパラメータのインデックスなど、 ノード固有の詳細。
- トレース参照 (特定の条件下でのみ
debug=True)。これは、ノードをその元のボックスに結びつけるアノテーションキーおよび参照文字列のことです。
プロットをクリックし、個々のノードにカーソルを合わせると、ソースコードを読まずにそのノードの役割を把握できる最も手っ取り早い方法です。
Samplexの概要
可視化を検証する前に、ノード数、必要な入力、および期待される出力について、簡潔なテキストによる概要を print(samplex) 説明します。
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.
レジスタを検査する
passing は keep_registers=True 、サンプリング終了時点で有効な中間オブジェクト VirtualRegister を保持 sample() し、それらを . に格納します outputs.metadata["registers"]。 各レジスタは、仮想群の要素からなる 2D 配列(形状は (num_subsystems, num_randomizations)、末尾にゲート形状の次元が付く場合あり)です。
これは、仮想ゲートが正しく結合されていることを確認したり、回転角に合成される前の生のパウリサンプルやユニタリーサンプルを検査したりするのに役立ちます。
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)
特定のレジスタの最終状態の内容を確認することができます。 次の例では、レジスタの型は です PauliRegister。 保存形式の詳細については、 API documentation(/apidocs/index)__ を参照してください。
print("lhs_0:", outputs.metadata["registers"]["lhs_0"])
outputs.metadata["registers"]["lhs_0"].virtual_gateslhs_0: PauliRegister(<4, 3>)
array([[0, 0, 1],
[3, 1, 1],
[3, 0, 0],
[1, 0, 0]], dtype=uint8)
Samplexノードとテンプレートパラメータの対応関係
によって返される配列 outputs["parameter_values"] の形状は sample() であり (num_randomizations, N)、ここで は と一致 N する len(template.parameters)。 各列は、テンプレート回路の1つのパラメータに対応しています。i番目の列には、が記入されます template.parameters[i]。
コレクション CollectTemplateValues ノードは、仮想レジスタとテンプレートパラメータをつなぐ役割を果たします。 このような各ノードは、出力配列のどの列に書き込むかを記録したインデックス情報を保持しています。 これにより、どの仮想ゲートサブシステムがどのテンプレートパラメータを駆動しているかを追跡できるようになります。
サンプリングされたパラメータ値をテンプレート回路にバインドし、実験を実行する方法については、『 {Samplex inputs and outputs}samplex-io 』ガイドを参照してください。
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]]
自動タグ付け
を使用すると generate_boxing_pass_manager()、 add_tags パラメータによって、すべてのボックスに自動的に アノテーション Tag が追加されます。 3つのモードが利用可能です:
"unique_instance": 回路の順序に従って、ボックスに連番(ref``t0,t1, ……)を割り当てます。 すべてのボックスには、その構造にかかわらずref、固有の識別子が割り当てられます。"unique_box": 各ボックスの内容について構造的ハッシュを計算し、構造的に同等のすべてのボックスrefに同じ値を割り当てます。 これは、位置ではなく種類ごとにボックスをグループ化するのに役立ちます。"noise_ref": 各ボックスの 注釈refInjectNoiseから をコピーし、注釈があるボックスのみにタグを付けます。 これは、すでに意味のあるノイズのリファレンスが存在する場合に役立ちます。
次の例では、構造的に等価な2つのCXボックスからなる回路 "unique_box" に、および "unique_instance" を適用しています。 の場合 "unique_instance"、それぞれが異なる を割り当てられるのに対し ref、の場合 "unique_box"、それらは1つの を共有する。
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