Skip to main content
IBM Quantum Platform

トランスパイラ段階

  • このページのコードは、以下の要件に基づいて開発された。 これらのバージョンまたは新しいバージョンの使用をお勧めします。

    qiskit[all]~=2.5.1
    

このページでは、Qiskit SDKにあらかじめ組み込まれているトランスパイルパイプラインのステージについて説明します。 ステージは6つある:

  1. init
  2. layout
  3. routing
  4. translation
  5. optimization
  6. scheduling

この generate_preset_pass_manager 関数は、これらのステージで構成されるプリセット・ステージド・パス・マネージャーを作成する。 各ステージを構成する具体的なパスは、 generate_preset_pass_manager に渡される引数によって決まる。 optimization_level は、指定しなければならない位置引数であり、0、1、2、3のいずれかの整数である。 値が大きいほど、最適化は重くなるが、コストがかかることを示す( Transpilation defaults and configuration optionsを参照)。

回路をトランスパイルする推奨される方法は、 パス・マネージャーを使ったトランスパイルで説明されているように、プリセット・ステージド・パス・マネージャーを作成し、そのパス・マネージャーを回路上で実行することです。 しかし、よりシンプルだがカスタマイズ性の低い代替案として transpile 関数を使うことだ。 この関数は、回路を直接引数として受け取る。 generate_preset_pass_manager と同様、使用される特定のトランスパイラ・パスは、 transpile に渡される optimization_level のような引数に依存する。 実際、 transpile 関数は内部で generate_preset_pass_manager を呼び出し、プリセットのステージド・パス・マネージャーを作成し、回路上で実行する。


初期化段階

この最初のステージは、デフォルトではほとんど何もせず、独自の初期最適化を含めたい場合に主に役立つ。 ほとんどのレイアウトと配線アルゴリズムは、1量子ビットと2量子ビットのゲートでしか動作しないように設計されているため、この段階は、2量子ビット以上で動作するゲートを、1量子ビットか2量子ビットだけで動作するゲートに変換するためにも使われる。

この段階での独自の初期最適化の実装については、プラグインとパスマネージャーのカスタマイズのセクションを参照してください。


レイアウト段階

次の段階では、回路が送信されるバックエンドのレイアウトや接続構成を検討します。 一般的に、量子回路とは、その量子ビットが、計算に使用される実際の量子ビットの「仮想的」あるいは「論理的」な表現である抽象的な実体である。 一連のゲートを実行するには、実際の量子デバイスにおいて、「仮想」量子ビットと「物理」量子ビットとの一対一の対応付けが必要である。 このマッピングはオブジェクト Layout として保存され、バックエンドの命令セットアーキテクチャ(ISA) 内で定義された制約の一部を構成しています。

この図は、ワイヤ表現から、QPU上で量子ビットがどのように接続されているかを表す図への量子ビットのマッピングを示しています。
量子ビットのマッピング

マッピングの選択は、入力回路をデバイス・トポロジーにマッピングするために必要なSWAP演算の回数を最小化し、最もよく較正された量子ビットを確実に使用するために極めて重要である。 この段階が重要であるため、プリセット・パスのマネージャーは最適なレイアウトを見つけるためにいくつかの異なる方法を試す。 まず、"完璧な "レイアウト(SWAP操作を必要としないレイアウト)を見つけようとするステップと、完璧なレイアウトが見つからない場合に使用する最適なレイアウトを見つけようとするヒューリスティックなステップである。 この最初のステップでは、通常2つの Passes

  • TrivialLayout: 各仮想量子ビットをデバイス上の同じ番号の物理量子ビットに単純にマッピングします(つまり、 [0113] -> [0113] )。これは、 optimzation_level=1 完璧なレイアウトを見つけようとします。 失敗した場合は、 VF2Layout
  • VF2Layout:これは AnalysisPass 、この段階を VF2++ アルゴリズムによって解決される部分グラフ同型問題として扱うことによって、理想的なレイアウトを選択するものである。 複数のレイアウトが見つかった場合、スコアリング・ヒューリスティックが実行され、平均誤差が最も小さいマッピングが選択される。

そしてヒューリスティック・ステージでは、デフォルトで2パスが使用される:

  • DenseLayout:最大の接続性を持ち、回路と同じ数の量子ビットを持つデバイスのサブグラフを見つける(回路内に制御フロー操作( IfElseOp など)が存在する場合、最適化レベル1に使用)。
  • SabreLayoutこのパスは、初期のランダムなレイアウトから開始し、アルゴリズム SabreSwap を繰り返し実行することでレイアウトを選択します。 このパスは、最適化レベル1、2、3においてのみ使用され、パーフェクトレイアウトが パス VF2Layout によって見出されない場合に限り適用される。 このアルゴリズムの詳細については、論文「 arXiv:1809.02573 」を参照してください。

ルーティング段階

量子デバイス上で直接接続されていない量子ビット間の2量子ビットゲートを実装するためには、1つ以上のSWAPゲートを回路に挿入し、デバイスのゲートマップ上で隣接するまで量子ビットの状態を移動させる必要がある。 各SWAPゲートは、高価でノイズの多い動作になる。 したがって、ある回路をあるデバイスにマッピングするのに必要なSWAPゲートの最小数を見つけることは、トランスパイルプロセスにおける重要なステップである。 効率化のため、このステージはデフォルトでレイアウトステージと一緒に計算されるのが一般的だが、論理的には別物である。 レイアウトステージは、使用するハードウェア量子ビットを選択し、 ルーティングステージは、選択されたレイアウトを使用して回路を実行するために、適切な量のSWAPゲートを挿入する。

しかし、最適なSWAPマッピングを見つけるのは難しい。 実際、この問題はNP困難問題であり、最小の量子デバイスと入力回路を除いて、計算には法外なコストがかかる。 これを回避するために、Qiskitは SabreSwap と呼ばれる確率的発見的アルゴリズムを使って、必ずしも最適ではないが、良いSWAPマッピングを計算します。 確率論的手法を使うということは、生成される回路が繰り返し実行されても同じであるとは保証されないということである。 実際、同じ回路を繰り返し実行すると、回路の深さと出力のゲート数の分布が生じる。 このため、多くのユーザーは、ルーティング機能(または StagedPassManager 全体)を何度も実行し、出力の分布から最も深さの浅い回路を選択する。

例えば、15量子ビットのGHZ回路を "悪い"(切断された) initial_layout を使って100回実行したとしよう。

import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2

backend = GenericBackendV2(15)


ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))

depths = []
for seed in range(100):
    pass_manager = generate_preset_pass_manager(
        optimization_level=1,
        backend=backend,
        layout_method="trivial",  # Fixed layout mapped in circuit order
        seed_transpiler=seed,  # For reproducible results
    )
    depths.append(pass_manager.run(ghz).depth())

plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)

Output:

Text(0, 0.5, 'Counts')
Output of the previous code cell

この幅広い分布は、SWAPマッパーが最適なマッピングを計算することがいかに難しいかを示している。 いくつかのヒントを得るために、実行されている回路とハードウェア上で選択された量子ビットの両方を見てみよう。

ghz.draw("mpl", idle_wires=False)

Output:

Output of the previous code cell
from qiskit.visualization import plot_circuit_layout

# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)

Output:

Output of the previous code cell

ご覧のように、この回路は接続グラフ上で非常に離れている量子ビット0と14の間で2量子ビットゲートを実行しなければならない。 したがって、この回路を実行するには、 SabreSwap パスを使ってすべての2量子ビットゲートを実行するためにSWAPゲートを挿入する必要がある。

また、 SabreSwap アルゴリズムは、前段階の大きな SabreLayout 方式とは異なることに注意。 デフォルトでは、 SabreLayout 、レイアウトと配線の両方が実行され、変換された回路が返される。 これは、パスの APIリファレンスページに明記されている、いくつかの特別な技術的理由によるものである。


翻訳段階

量子回路を作成する際には、任意の量子ゲート(ユニタリ演算)を自由に使用できるほか、量子ビットの測定やリセット命令といったゲート以外の操作も組み合わせて使用できます。 しかし、ほとんどの量子デバイスは、本来、ごく少数の量子ゲートおよび非ゲート演算しかサポートしていない。 これらのネイティブゲートは、 ターゲットのISAの定義の一部であり、プリセットのこの段階では、回路で指定されたゲートを、指定 PassManagers されたバックエンドのネイティブベースのゲートに変換( またはアンロール )します。 これは、バックエンドによって回路を実行できるようにするという点で重要なステップですが、通常、回路の深さとゲート数が増加することにつながります。

このステージが何をするのかを説明するのに役立つ、2つの特別なケースが特に重要である。

  1. SWAPゲートがターゲット・バックエンドのネイティブ・ゲートでない場合、3つのCNOTゲートが必要となる:
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")

Output:

native gates:['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Output of the previous code cell

3つのCNOTゲートの積であるSWAPは、ノイズの多い量子デバイスで実行するには高価な演算である。 しかし、このような操作は、通常、多くのデバイスの限られたゲート接続性の中に回路を組み込むために必要である。 したがって、回路内のSWAPゲートの数を最小化することは、トランスパイルプロセスにおける主要な目標である。

  1. トッフォリゲート(controlled-controlled-not gate、ccx )は、3量子ビットゲートである。 基底ゲートセットには1量子ビットと2量子ビットのゲートしか含まれていないため、この演算は分解しなければならない。 しかし、かなりコストがかかる:
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")

Output:

Output of the previous code cell

量子回路のトッフォリゲート1つに対して、ハードウェアは最大6つのCNOTゲートと数個のシングル量子ビットゲートを実行する可能性がある。 この例は、複数のトフォリ・ゲートを使用するアルゴリズムは、最終的に回路深度が大きくなり、ノイズの影響を大きく受けることを示している。


最適化段階

この段階では、量子回路をターゲット・デバイスの基本ゲート・セットに分解することが中心で、レイアウトや配線段階で深さが増すのと戦わなければならない。 幸いなことに、ゲートを組み合わせたり取り除いたりすることで回路を最適化するルーチンは数多く存在する。 場合によっては、これらの方法は非常に効果的で、レイアウトとハードウェア・トポロジーへの配線後でさえ、出力回路は入力回路よりも深さが浅くなる。 また、ノイズの多いデバイスでは計算が難しい場合もある。 この段階から、さまざまな最適化レベルの違いが出てくる。

さらにこの段階では、回路内のすべての命令がターゲット・バックエンドで利用可能な基底ゲートで構成されていることを確認するために、いくつかの最終チェックも実行する。

GHZステートを使用した以下の例は、最適化レベル設定の違いによる回路の深さとゲート数への影響を示しています。

Note

ストキャスティックなSWAPマッパーのため、トランスピレーション出力は変化する。 したがって、以下の数値はコードを実行するたびに変化する可能性が高い。

15qubitのGHZ状態
15qubitのGHZ状態 移調前

以下のコードでは、15qubitのGHZステートを構築し、結果として得られる回路深さ、ゲート数、および多qubitゲート数の観点から、 optimization_levels

ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))

depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
    pass_manager = generate_preset_pass_manager(
        optimization_level=level,
        backend=backend,
        seed_transpiler=1234,
    )
    circ = pass_manager.run(ghz)
    depths.append(circ.depth())
    gate_counts.append(sum(circ.count_ops().values()))
    multiqubit_gate_counts.append(circ.count_ops()["cx"])

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()

Output:

Output of the previous code cell

スケジューリング

この最後のステージは、明示的に呼び出された場合にのみ実行され(Initステージと同様)、デフォルトでは実行されない(ただし、 generate_preset_pass_manager を呼び出す際に scheduling_method 引数を設定することで、メソッドを指定できる)。 スケジューリング・ステージは通常、回路がターゲット・ベースに変換され、デバイスにマッピングされ、最適化された時点で使用される。 これらのパスは、回路内のすべてのアイドル時間を考慮することに重点を置いている。 高いレベルでは、スケジューリング・パスは、ゲート実行間のアイドル時間を考慮し、回路がバックエンドで実行される時間を検査するために、明示的に遅延命令を挿入すると考えることができる。

以下に例を示します。

ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0, range(1, 5))


# Use fake backend
backend = GenericBackendV2(5)

# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    scheduling_method="asap",
    seed_transpiler=1234,
)


circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)

Output:

Output of the previous code cell
遅延指示回路

トランスパイラーは、各量子ビットのアイドル時間を考慮し、 Delay 。 回路のタイミングをよりよく知るために、 timeline.draw()

timeline.draw () 同じ回路の図

回路のスケジューリングには、解析と制約マッピングの2つのパートがあり、その後にパディング・パスが続く。 最初のパートでは、スケジューリング解析パス(デフォルトではこれは ALAPSchedulingAnalysis)を実行する必要があります。これは回路を解析し、回路内の各命令の開始時刻をスケジュールに記録します。 回路に初期スケジュールが設定されると、ターゲットとなるバックエンドのタイミング制約を考慮するために、追加のパスを実行することができる。 最後に PadDelay または PadDynamicalDecoupling などのパディングパスが実行できる。


次のステップ

推奨事項
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。