Skip to main content
IBM Quantum Platform

デバッグおよびトレース

この関 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")
../_images/f6b6fe7b60a39ba897814008443fa79b66213005462ab09f49ffc89aab1f3668.png

テンプレート回路内のバリアラベル

上記の回路を呼び出す 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)
../_images/e533d70a29504005a636aa27306d3ce3b1d2488358ac6a53f0674f2934698ab8.png

バリアラベルは、プログラムによって抽出することも可能です:

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つの段階に分けて行われます:

  1. すべてのサンプリングノードが最初に(並列で)実行されます。
  2. 次に、評価ノードがトポロジカル順に実行されます(各世代内では並列に実行されます)。
  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_gates
lhs_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
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。