{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "1ed79d34-1831-47cb-9985-5bb7dacc70e0",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"SABRE を用いたトランスパイレーションの最適化\"\n",
        "description: \"大規模なハードウェア実行に向けて、SABREによるレイアウトおよび配線を用いて量子回路を最適化する。\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore ylabel xlabel fontsize sharex edgecolor, fontweight, elinewidth, ecolor, prerouter, ylim */}\n",
        "\n",
        "<span id=\"transpilation-optimization-with-sabre\" />\n",
        "\n",
        "# SABRE を用いたトランスパイレーションの最適化\n",
        "\n",
        "*推定実行時間：Heron r2 プロセッサで 1 分（注：これはあくまで推定値です。 （実行時間は異なる場合があります。）*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d6834016-1525-42d7-aa21-0fef9d957ecd",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## 学習成果\n",
        "\n",
        "このチュートリアルを学習し終える頃には、以下の点を理解できるようになるはずです：\n",
        "\n",
        "* トランスパイラリングの品質を向上させるためのSABREパラメータ（`layout_trials`, `swap_trials`, `max_iterations`）の設定方法\n",
        "* トランスパイレーションの実行時間と回路の品質（深さおよびゲート数）とのトレードオフ\n",
        "* SABREルーティングヒューリスティック（`basic`, `decay`, `lookahead`）をカスタマイズする方法と、ハードウェア上での性能比較\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## 前提条件\n",
        "\n",
        "このチュートリアルを進める前に、以下のトピックについて理解しておいていただくことをお勧めします：\n",
        "\n",
        "* [回路のトランスパイル](/docs/guides/transpile) ：Qiskitにおけるトランスパイルの概要\n",
        "* [トランスパイラの各段階](/docs/guides/transpiler-stages) ：レイアウト段階と配線段階\n",
        "* [プリセットのパスマネージャーの設定](/docs/guides/transpile-with-pass-managers) ：最適化レベルをカスタマイズする\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## 背景\n",
        "\n",
        "トランスパイレーションとは、量子回路を特定の量子ハードウェアと互換性のある形式に変換することです。 重要な段階は2つあり、 **1** つは量子ビットのレイアウトの決定（論理量子ビットを物理量子ビットにマッピングすること）、 **もう** 1つはゲートの配線（マルチ量子ビットゲートがデバイスの接続性を考慮するようにSWAPゲートを挿入すること）である。\n",
        "\n",
        "**SABRE** （ *SWAPベースの双方向ヒューリスティック探索アルゴリズム* ）は、レイアウトと配線の両方を最適化します。 これは、 IBM® のHeronプロセッサのように、複雑な結合マップを持つデバイス上での大規模回路（100クビット以上）において特に効果的です。 SABREはSWAPゲートの数を最小限に抑え、回路の深さを低減することで、実行の忠実度を向上させます。 [LightSABRE](https://arxiv.org/abs/2409.08368) アルゴリズムにおける最近の改良により、実行時間とゲート数がさらに削減されました。\n",
        "\n",
        "このチュートリアルでは、まずさまざまなパラメータを設定 `SabreLayout` して、小さなGHZ回路を最適化し、実行の忠実度に与える影響を確認します。 次に、実際のハードウェア上で、SABREのルーティングヒューリスティックを大規模に比較検証します。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50cf9d9f-c875-49d5-83b0-c691363615ab",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 要件\n",
        "\n",
        "このチュートリアルを始める前に、以下のものがインストールされていることを確認してください：\n",
        "\n",
        "* Qiskit SDK v2.0 またはそれ以降のバージョンで、 [可視化](/docs/api/qiskit/visualization)機能をサポートしているもの\n",
        "* Qiskit Runtime v0.22 またはそれ以降 (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d9ad2c3-1b10-4a22-8b75-608869be0ac7",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## セットアップ\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6d93c123-b92e-434d-8361-8115509e6d5f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using backend: ibm_kingston\n"
          ]
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import EstimatorOptions\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "from qiskit_aer.primitives import EstimatorV2 as AerEstimator\n",
        "from qiskit.transpiler.passes import (\n",
        "    SabreLayout,\n",
        "    SabreSwap,\n",
        "    BarrierBeforeFinalMeasurements,\n",
        "    StarPreRouting,\n",
        ")\n",
        "from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.passmanager.flow_controllers import ConditionalController\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import time\n",
        "\n",
        "seed = 42\n",
        "\n",
        "service = QiskitRuntimeService(\n",
        "    channel=\"ibm_cloud\",\n",
        "    token=\"<YOUR_API_TOKEN>\",  # Replace with your actual API token\n",
        "    instance=\"<YOUR_INSTANCE_NAME>\",  # Replace with your instance name if needed\n",
        ")\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "\n",
        "print(f\"Using backend: {backend.name}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c34842c6-5e1b-4658-8e91-15149280c783",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## 小規模シミュレータの例\n",
        "\n",
        "このセクションでは、実際のバックエンドのノイズモデルに基づいた**ノイズ付きシミュレータ**を用いて、さまざまな `SabreLayout` 設定がトランスパイルの品質と実行の忠実度にどのような影響を与えるかを示します。 実際のハードウェア校正データから導出されたノイズモデルを併用 `qiskit_aer` することで、ハードウェアクレジットを消費することなく、トランスパイレーションのテストを行うことができます。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2bcc2b2-727b-447f-b680-a23e3b9e0c3c",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### ステップ1：古典的な入力を量子問題にマッピングする\n",
        "\n",
        "15キュービットからなる**スター型トポロジーのGHZ回路**を構築する。 最初の量子ビットがハブとなり、CNOTゲートを介して他のすべての量子ビットと直接接続されています。 このトポロジーは、デバイスの結合マップに単純に写像されないため、レイアウト上の難しい問題を引き起こします。\n",
        "\n",
        "また、量子ビットのペア間のエンタングルメント相関 $\\langle Z_0 Z_i \\rangle$ を測定するための演算子も定義 `ZZ` する。\n",
        "\n",
        "![ghz\\_star\\_topology.png](https://quantum.cloud.ibm.com/docs/images/tutorials/transpilation-optimizations-with-sabre/ghz_star_topology.avif)\n",
        "\n",
        "<Admonition type=\"note\" title=\"回路の構成が分かっているときは\">\n",
        "  SABREは汎用アルゴリズムであり、回路構造についていかなる仮定も行いません。 このスタートポロジーのGHZ回路については、実際に最適な配線方法が知られている。すなわち、この [`StarPreRouting`](/docs/api/qiskit/qiskit.transpiler.passes.StarPreRouting) パスはスター型のサブ回路を検出し、それらを直線的なチェーンに書き換える。このチェーンは、十分に長い直線経路を持つ任意のバックエンドに直接マッピングされる。 このチュートリアルでは、任意の回路に対応していることからSABREに焦点を当てていますが、回路に明確な特殊な構造があることが分かっている場合は、配線前に前述のような `StarPreRouting` 特化された処理を適用することで、どのようなヒューリスティック探索よりも優れた性能を発揮できる場合があります。\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "08a8c4df-b1a5-45b7-9808-ca4e5d7631d8",
      "metadata": {},
      "outputs": [],
      "source": [
        "num_qubits_sim = 15\n",
        "\n",
        "# Create star-topology GHZ circuit\n",
        "qc_sim = QuantumCircuit(num_qubits_sim)\n",
        "qc_sim.h(0)\n",
        "for i in range(1, num_qubits_sim):\n",
        "    qc_sim.cx(0, i)\n",
        "qc_sim.measure_all()\n",
        "\n",
        "# ZZ operators: Z on qubit 0 and qubit i, identity elsewhere\n",
        "operator_strings_sim = [\n",
        "    \"Z\" + \"I\" * i + \"Z\" + \"I\" * (num_qubits_sim - 2 - i)\n",
        "    for i in range(num_qubits_sim - 1)\n",
        "]\n",
        "operators_sim = [SparsePauliOp(op) for op in operator_strings_sim]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25ac8d92-6362-4d4e-a133-2914703df658",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### ステップ2：量子ハードウェア実行に向けた問題の最適化\n",
        "\n",
        "`SabreLayout`デフォルト `optimization_level=3` のプリセット・パス・マネージャーはすでにこれを使用していますが、デフォルト設定は控えめになっています。 より強力な設定が及ぼす影響を調査するため、そのパスは、より積極的な検索が行われるようカスタマイズ `SabreLayout` された設定に置き換えられ、レイアウト段階のその他のパスはすべて変更されません。 別の比較対象として、4番目のパス・マネージャーはデフォルト `SabreLayout` の設定を維持しつつ、初期化段階に を追加 [`StarPreRouting`](/docs/api/qiskit/qiskit.transpiler.passes.StarPreRouting) します。 `StarPreRouting` これは、 *構造を意識した*パスであり、スター型サブ回路を検出し、配線を行う前にそれらを線形チェーンに書き換えるものである。\n",
        "\n",
        "ワークフローは以下の通りです：\n",
        "\n",
        "1. デフォルトのパスマネージャーを**調べて**、がステージ内の `layout` どこ `SabreLayout` に位置しているかを確認してください。\n",
        "2. `pm.init += StarPreRouting()``PassManager.replace(index, passes=...)`そのパスを、を使用してカスタム `SabreLayout` インスタンスに**置き換え**、を使用してバリアントを `pm_star` 構築します。\n",
        "3. 4つのパスマネージャーをすべて**実行し**、メトリクスを比較してください。\n",
        "\n",
        "4つの構成は以下の通りです：\n",
        "\n",
        "| 構成             | 説明                                                                                           |\n",
        "| -------------- | -------------------------------------------------------------------------------------------- |\n",
        "| `pm_1` (デフォルト) | level-3 のデフォルトプリセット（`SabreLayout` , `max_iterations=4` `layout_trials=20`, `swap_trials=20`） |\n",
        "| `pm_2`         | カスタム `SabreLayout` (`max_iterations=4`, `layout_trials=200`, `swap_trials=200`)              |\n",
        "| `pm_3`         | カスタム `SabreLayout` (`max_iterations=8`, `layout_trials=200`, `swap_trials=200`)              |\n",
        "| `pm_star`      | 初期化段階に が `StarPreRouting` 追加されたデフォルトのプリセット                                                   |\n",
        "\n",
        "**SABREの主なパラメータ：**\n",
        "\n",
        "* **`layout_trials` / `swap_trials`**: SABRE が検討するレイアウト候補およびルーティング案の数を制御します。 試行回数を増やすことで、SABREはより広範な探索空間を網羅し、より優れた解が見つかる可能性が高まります。\n",
        "* **`max_iterations`**: SABREが各候補に対して実行する、順方向・逆方向のルーティング精緻化サイクルの回数を制御します。 SABREは配線フィードバックから学習することでレイアウトを反復的に改善するため、反復回数が多ければ多いほど、改善効果が高まります。\n",
        "\n",
        "どちらもトランスパイレーションに時間がかかるという代償を伴いますが、その結果得られる回路はより短くなり、使用するゲート数も少なくなるため、実際のハードウェア上でのデコヒーレンスやゲートエラーを直接的に低減することができます。\n",
        "\n",
        "**ステップ 2a: デフォルトのパスマネージャーを確認します。** `PassManager`A `StagedPassManager` は、各々がそれ自体が a である段階 (`init`, `layout`, `routing`, `translation`, `optimization`, `scheduling`) から構成される。 ステージ上で呼び出す `.draw()` と、そのパスがグラフとして表示されるため、どこ `SabreLayout` にあるのかを確認できます。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "b40fe1e0-41cd-4e8b-acb9-801872d35f1f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/b40fe1e0-41cd-4e8b-acb9-801872d35f1f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Build the default pass manager (no modifications yet)\n",
        "pm_1 = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=seed\n",
        ")\n",
        "\n",
        "# Visualize the layout stage to see where SabreLayout sits\n",
        "pm_1.layout.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f9e5393-af3b-4d3d-a0d8-d20585b02895",
      "metadata": {},
      "source": [
        "上の図では、カスタマイズしたいパスは `SabreLayout` 、レイアウトステージの \\*\\*\\[2番目の]\\*\\*位置にある 内にあります。 `ConditionalController` そのコントローラーには、次の2つの機能があります：\n",
        "\n",
        "* ゲート `SabreLayout` 処理が行われるため、 \\[1] が完全なマッピングを見つけられなかった場合に `VF2Layout` のみ実行されます（それ以外の場合は、完全な VF2 のレイアウトが維持されます）。\n",
        "* これに先立ち `SabreLayout` 、 SabreLayout's の内部ルーティング中に測定値の順序が入れ替わらないようにするパスが実行されます `BarrierBeforeFinalMeasurements` 。\n",
        "\n",
        "`replace(index=2, passes=sl_2)`単に すれば、どちらの挙動も無効になります。 それらを保持するために、交換する前に、当社の特注 `SabreLayout` 品を同じ `ConditionalController` 包装材（同じ状態と保護層を保ったまま）で再度包み直します。\n",
        "\n",
        "**ステップ 2b: ：カスタム `SabreLayout` パスを構築し、デフォルトのパスを置き換えます。**\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "79075a21-8f36-4fd9-9d0d-bd0e97395b60",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/79075a21-8f36-4fd9-9d0d-bd0e97395b60-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "cmap = backend.coupling_map\n",
        "\n",
        "# Custom SabreLayout passes with more aggressive search\n",
        "sl_2 = SabreLayout(\n",
        "    coupling_map=cmap,\n",
        "    seed=seed,\n",
        "    max_iterations=4,\n",
        "    layout_trials=200,\n",
        "    swap_trials=200,\n",
        ")\n",
        "sl_3 = SabreLayout(\n",
        "    coupling_map=cmap,\n",
        "    seed=seed,\n",
        "    max_iterations=8,\n",
        "    layout_trials=200,\n",
        "    swap_trials=200,\n",
        ")\n",
        "\n",
        "\n",
        "# Same condition the preset uses: only run SabreLayout when VF2Layout did not\n",
        "# find a perfect mapping. This preserves any perfect layout VF2 produced at [1].\n",
        "def _vf2_match_not_found(property_set):\n",
        "    if property_set[\"layout\"] is None:\n",
        "        return True\n",
        "    return (\n",
        "        property_set[\"VF2Layout_stop_reason\"] is not None\n",
        "        and property_set[\"VF2Layout_stop_reason\"]\n",
        "        is not VF2LayoutStopReason.SOLUTION_FOUND\n",
        "    )\n",
        "\n",
        "\n",
        "def wrap_sabre(sabre_pass):\n",
        "    \"\"\"Re-wrap a SabreLayout in the original ConditionalController + barrier.\"\"\"\n",
        "    return ConditionalController(\n",
        "        [\n",
        "            BarrierBeforeFinalMeasurements(\n",
        "                \"qiskit.transpiler.internal.routing.protection.barrier\"\n",
        "            ),\n",
        "            sabre_pass,\n",
        "        ],\n",
        "        condition=_vf2_match_not_found,\n",
        "    )\n",
        "\n",
        "\n",
        "# Build two fresh pass managers and swap in the wrapped custom SabreLayout at index 2\n",
        "pm_2 = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=seed\n",
        ")\n",
        "pm_3 = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=seed\n",
        ")\n",
        "pm_2.layout.replace(index=2, passes=wrap_sabre(sl_2))\n",
        "pm_3.layout.replace(index=2, passes=wrap_sabre(sl_3))\n",
        "\n",
        "# Build pm_star: default preset with StarPreRouting added to the init stage\n",
        "pm_star = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=seed\n",
        ")\n",
        "pm_star.init += StarPreRouting()\n",
        "\n",
        "# Visualize pm_3 after replacement (pm_2 has the same structure, only max_iterations differs)\n",
        "pm_3.layout.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "378a5829-b18b-4359-827f-b052c7cc1b89",
      "metadata": {},
      "source": [
        "`max_iterations=4``pm_3`位置 **\\[2]** は再び になりました `ConditionalController` 。形状はデフォルトのものと同じですが、内部の `SabreLayout` はカスタムのものになっています（、 `swap_trials=200``layout_trials=200`、および `max_iterations=8` が に設定されており、 `pm_2` は を除いて同一です）。 `pm_2`保護バリアとゲーティングは `_vf2_match_not_found` 維持されているため、/`pm_3` と `pm_1` の唯一の違いは、SABREの構成そのものです。 `pm_star` デフォルト `SabreLayout` の設定を維持し、initステージの最後にのみを追加 `StarPreRouting` します。\n",
        "\n",
        "**ステップ 2c: 各パスマネージャーを実行し、結果を比較する。**\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "fd52f8dd-862b-46e2-b93d-0b35f47a3d83",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "pm_1 (4,20,20): 2Q Depth 38, Size 183, Time 0.01s\n",
            "pm_2 (4,200,200): 2Q Depth 36, Size 183, Time 0.15s\n",
            "pm_3 (8,200,200): 2Q Depth 30, Size 158, Time 0.16s\n",
            "pm_star (default + StarPreRouting): 2Q Depth 26, Size 160, Time 0.01s\n",
            "\n",
            "Improvement vs. default (pm_1):\n",
            "  pm_2 (4,200,200): 2Q depth +5.3%, size +0.0%\n",
            "  pm_3 (8,200,200): 2Q depth +21.1%, size +13.7%\n",
            "  pm_star (default + StarPreRouting): 2Q depth +31.6%, size +12.6%\n"
          ]
        }
      ],
      "source": [
        "results_sim = {}\n",
        "for name, pm in [\n",
        "    (\"pm_1 (4,20,20)\", pm_1),\n",
        "    (\"pm_2 (4,200,200)\", pm_2),\n",
        "    (\"pm_3 (8,200,200)\", pm_3),\n",
        "    (\"pm_star (default + StarPreRouting)\", pm_star),\n",
        "]:\n",
        "    t0 = time.time()\n",
        "    tqc = pm.run(qc_sim)\n",
        "    elapsed = time.time() - t0\n",
        "    depth = tqc.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    size = tqc.size()\n",
        "    ops_mapped = [op.apply_layout(tqc.layout) for op in operators_sim]\n",
        "    results_sim[name] = {\n",
        "        \"tqc\": tqc,\n",
        "        \"ops\": ops_mapped,\n",
        "        \"depth\": depth,\n",
        "        \"size\": size,\n",
        "        \"time\": elapsed,\n",
        "    }\n",
        "    print(f\"{name}: 2Q Depth {depth}, Size {size}, Time {elapsed:.2f}s\")\n",
        "\n",
        "# Print improvement relative to default (pm_1)\n",
        "baseline = results_sim[\"pm_1 (4,20,20)\"]\n",
        "print(\"\\nImprovement vs. default (pm_1):\")\n",
        "for name in [\n",
        "    \"pm_2 (4,200,200)\",\n",
        "    \"pm_3 (8,200,200)\",\n",
        "    \"pm_star (default + StarPreRouting)\",\n",
        "]:\n",
        "    r = results_sim[name]\n",
        "    depth_pct = (baseline[\"depth\"] - r[\"depth\"]) / baseline[\"depth\"] * 100\n",
        "    size_pct = (baseline[\"size\"] - r[\"size\"]) / baseline[\"size\"] * 100\n",
        "    print(f\"  {name}: 2Q depth {depth_pct:+.1f}%, size {size_pct:+.1f}%\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0c27cbdb-c065-4a36-a0ef-6c9572c7a4a5",
      "metadata": {},
      "source": [
        "3つの修正済みパスマネージャーはすべて、デフォルトよりも 2Q の深さが浅い回路を生成した。 `pm_3`攻撃的なSABRE構成（`pm_2` および）は、トランスピレーション時間を長くする代わりに探索範囲を広げていますが、は `pm_star` 回路のスター構造を活用し、追加のトランスピレーションコストを一切かけずに、さらに浅い検索結果を生み出します。 具体的な改善効果は実行ごとに異なりますが、全体的な傾向は一貫しています。すなわち、SABREの試行回数や反復回数を増やすことで、ヒューリスティック探索の対象範囲を広げることができ、また、回路の形状が一致する場合は、のような `StarPreRouting` 構造認識型のパスによって、その探索を完全に回避することができます。\n",
        "\n",
        "この小規模なスケール（15キュービット）であっても、改善の余地は十分にあり、3つのアプローチすべてがデフォルトの性能を上回っている。 大規模な回路（100クビット以上）では、探索空間が飛躍的に拡大し、大規模回路のセクションで後述するように、試行回数の増加と構造を考慮したパスの両方の利点がより顕著になります。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "bf75c45a-2c3e-4ef6-8336-0b3f69e6e8fb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/bf75c45a-2c3e-4ef6-8336-0b3f69e6e8fb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "pm_names = list(results_sim.keys())\n",
        "depths = [results_sim[n][\"depth\"] for n in pm_names]\n",
        "sizes = [results_sim[n][\"size\"] for n in pm_names]\n",
        "times = [results_sim[n][\"time\"] for n in pm_names]\n",
        "colors = [\"#404080\", \"#2a9d8f\", \"#a8d05e\", \"#e29bdd\"]\n",
        "x = np.arange(len(pm_names))\n",
        "\n",
        "fig, axs = plt.subplots(1, 3, figsize=(14, 5))\n",
        "\n",
        "# 2Q Depth\n",
        "bars = axs[0].bar(x, depths, color=colors)\n",
        "axs[0].set_ylabel(\"2Q Depth\", fontsize=11)\n",
        "axs[0].set_title(\"Two-Qubit Gate Depth\", fontsize=13)\n",
        "axs[0].set_ylim(0, max(depths) * 1.2)\n",
        "for bar, val in zip(bars, depths):\n",
        "    axs[0].text(\n",
        "        bar.get_x() + bar.get_width() / 2,\n",
        "        bar.get_height() + max(depths) * 0.02,\n",
        "        str(val),\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\",\n",
        "        fontsize=11,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "for i in range(1, len(depths)):\n",
        "    pct = (depths[0] - depths[i]) / depths[0] * 100\n",
        "    if pct != 0:\n",
        "        axs[0].text(\n",
        "            bars[i].get_x() + bars[i].get_width() / 2,\n",
        "            bars[i].get_height() / 2,\n",
        "            f\"{pct:+.0f}%\",\n",
        "            ha=\"center\",\n",
        "            va=\"center\",\n",
        "            fontsize=10,\n",
        "            color=\"white\",\n",
        "            fontweight=\"bold\",\n",
        "        )\n",
        "\n",
        "# Size\n",
        "bars = axs[1].bar(x, sizes, color=colors)\n",
        "axs[1].set_ylabel(\"Gate Count\", fontsize=11)\n",
        "axs[1].set_title(\"Circuit Size\", fontsize=13)\n",
        "axs[1].set_ylim(0, max(sizes) * 1.2)\n",
        "for bar, val in zip(bars, sizes):\n",
        "    axs[1].text(\n",
        "        bar.get_x() + bar.get_width() / 2,\n",
        "        bar.get_height() + max(sizes) * 0.02,\n",
        "        str(val),\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\",\n",
        "        fontsize=11,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "for i in range(1, len(sizes)):\n",
        "    pct = (sizes[0] - sizes[i]) / sizes[0] * 100\n",
        "    if abs(pct) > 0.1:\n",
        "        axs[1].text(\n",
        "            bars[i].get_x() + bars[i].get_width() / 2,\n",
        "            bars[i].get_height() / 2,\n",
        "            f\"{pct:+.0f}%\",\n",
        "            ha=\"center\",\n",
        "            va=\"center\",\n",
        "            fontsize=10,\n",
        "            color=\"white\",\n",
        "            fontweight=\"bold\",\n",
        "        )\n",
        "\n",
        "# Time\n",
        "bars = axs[2].bar(x, times, color=colors)\n",
        "axs[2].set_ylabel(\"Time (s)\", fontsize=11)\n",
        "axs[2].set_title(\"Transpilation Time\", fontsize=13)\n",
        "axs[2].set_ylim(0, max(times) * 1.3)\n",
        "for bar, val in zip(bars, times):\n",
        "    axs[2].text(\n",
        "        bar.get_x() + bar.get_width() / 2,\n",
        "        bar.get_height() + max(times) * 0.03,\n",
        "        f\"{val:.2f}s\",\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\",\n",
        "        fontsize=11,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "\n",
        "for ax in axs:\n",
        "    ax.set_xticks(x)\n",
        "    ax.set_xticklabels(pm_names, fontsize=8, rotation=15)\n",
        "    ax.grid(axis=\"y\", linestyle=\"--\", alpha=0.5)\n",
        "\n",
        "plt.suptitle(\n",
        "    \"Transpilation quality vs. configuration\",\n",
        "    fontsize=14,\n",
        "    fontweight=\"bold\",\n",
        "    y=1.02,\n",
        ")\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a233965-1358-420b-8823-5f4e206090b4",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### ステップ3: `Qiskit primitives`を使用して実行する\n",
        "\n",
        "各トランスパイルされた回路について、実際のバックエンドから導出したノイズモデルを用いて、Aer `EstimatorV2` を使用して **10回**実行します。 シミュレーション結果にはランごとにばらつきがあるため、複数のランの結果を平均化することで、より信頼性の高い精度推定値が得られ、誤差棒を用いて統計的な不確実性を定量化することが可能になります。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "a91b9887-c8cf-48fd-a6fb-a5506d201f8d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Run 1/10 done\n",
            "Run 2/10 done\n",
            "Run 3/10 done\n",
            "Run 4/10 done\n",
            "Run 5/10 done\n",
            "Run 6/10 done\n",
            "Run 7/10 done\n",
            "Run 8/10 done\n",
            "Run 9/10 done\n",
            "Run 10/10 done\n",
            "pm_1 (4,20,20): mean fidelity = 0.9510 +/- 0.0094\n",
            "pm_2 (4,200,200): mean fidelity = 0.9513 +/- 0.0043\n",
            "pm_3 (8,200,200): mean fidelity = 0.9540 +/- 0.0065\n",
            "pm_star (default + StarPreRouting): mean fidelity = 0.9547 +/- 0.0072\n"
          ]
        }
      ],
      "source": [
        "# Create a noisy estimator from the real backend's noise model\n",
        "noisy_estimator = AerEstimator.from_backend(backend)\n",
        "\n",
        "num_runs = 10\n",
        "# sim_all_runs[name] = list of arrays, one per run\n",
        "sim_all_runs = {name: [] for name in results_sim}\n",
        "\n",
        "for run in range(num_runs):\n",
        "    for name, r in results_sim.items():\n",
        "        job = noisy_estimator.run([(r[\"tqc\"], r[\"ops\"])])\n",
        "        evs = list(job.result()[0].data.evs)\n",
        "        sim_all_runs[name].append(evs)\n",
        "    print(f\"Run {run + 1}/{num_runs} done\")\n",
        "\n",
        "# Compute mean and std across runs for each config\n",
        "sim_stats = {}\n",
        "for name in results_sim:\n",
        "    all_evs = np.array(sim_all_runs[name])  # shape (num_runs, num_operators)\n",
        "    sim_stats[name] = {\n",
        "        \"mean\": np.mean(all_evs, axis=0),\n",
        "        \"std\": np.std(all_evs, axis=0),\n",
        "        \"overall_mean\": np.mean(all_evs),\n",
        "        \"overall_std\": np.std(\n",
        "            np.mean(all_evs, axis=1)\n",
        "        ),  # std of per-run averages\n",
        "    }\n",
        "    print(\n",
        "        f\"{name}: mean fidelity = {sim_stats[name]['overall_mean']:.4f} +/- {sim_stats[name]['overall_std']:.4f}\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89e6c70f-4da4-470f-b0fa-2e2521c82f6f",
      "metadata": {},
      "source": [
        "これは小規模な回路であるため、4つの構成すべてにおいて、忠実度の値は比較的近い範囲に収まっています。 回路が十分に短いため、最適化が最も不十分なバージョンであっても、ハードウェアノイズによる影響はそれほど大きくありません。 平均フィデリティは、おおむね 2Q の深さと相関している。 `pm_3` 最も浅い2つの回路である `pm_star` と は、最も高いフィデリティを達成しており、誤差範囲内では実質的に同値である。 `pm_1``pm_2` は有用な反例である。 2Q の深さは よりも浅いにもかかわらず、その平均フィデリティもわずかに低くなっており、これは深さとフィデリティの関係が決定論的ではなく統計的なものであることを改めて示している。 レイアウトで選択される具体的な量子ビットや、実行時のそれらの量子ビットのキャリブレーションも重要である。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e601648b-8f5f-4111-ba35-f64af63da909",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### ステップ4：後処理を行い、結果を希望の古典形式で返す\n",
        "\n",
        "次に、量子ビット間の距離の関数として、エンタングルメント相関 $\\langle Z_0 Z_i \\rangle$ をプロットするとともに、単一の忠実度指標としての**平均相関**も併せてプロットする。 理想的な（ノイズのない）場合、すべての相関は1となる。 現実的なノイズが存在する場合、ゲートが1つ追加されるごとに誤差が生じ、時間ステップが1つ進むごとにデコヒーレンスが起こるため、深さが浅く、ゲート数（特に2量子ビットゲート）が少ないトランスパイルされた回路ほど、量子もつれをよりよく維持できるはずである。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "a6dac5ed-a963-458a-ada1-89c915f036e0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/a6dac5ed-a963-458a-ada1-89c915f036e0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "data_sim = list(range(1, len(operators_sim) + 1))\n",
        "markers = [\"o\", \"s\", \"^\", \"*\"]\n",
        "colors_line = [\"#404080\", \"#2a9d8f\", \"#a8d05e\", \"#e29bdd\"]\n",
        "\n",
        "fig, (ax1, ax2) = plt.subplots(\n",
        "    1, 2, figsize=(14, 5), gridspec_kw={\"width_ratios\": [2.5, 1]}\n",
        ")\n",
        "\n",
        "# Left: correlations vs distance with error bars (mean +/- 1 std)\n",
        "for (name, stats), marker, color in zip(\n",
        "    sim_stats.items(), markers, colors_line\n",
        "):\n",
        "    ax1.errorbar(\n",
        "        data_sim,\n",
        "        stats[\"mean\"],\n",
        "        yerr=stats[\"std\"],\n",
        "        marker=marker,\n",
        "        label=name,\n",
        "        color=color,\n",
        "        linewidth=2,\n",
        "        capsize=3,\n",
        "        capthick=1,\n",
        "        elinewidth=1,\n",
        "    )\n",
        "\n",
        "ax1.set_xlabel(\"Distance between qubits $i$\", fontsize=11)\n",
        "ax1.set_ylabel(r\"$\\langle Z_0 Z_i \\rangle$\", fontsize=11)\n",
        "ax1.set_title(\n",
        "    \"Entanglement correlations vs. qubit distance (avg. of 10 runs)\",\n",
        "    fontsize=12,\n",
        ")\n",
        "ax1.legend(fontsize=9)\n",
        "ax1.grid(alpha=0.3)\n",
        "\n",
        "# Right: mean correlation bar chart with error bars\n",
        "names = list(sim_stats.keys())\n",
        "means = [sim_stats[n][\"overall_mean\"] for n in names]\n",
        "stds = [sim_stats[n][\"overall_std\"] for n in names]\n",
        "x_bar = np.arange(len(names))\n",
        "bars = ax2.bar(\n",
        "    x_bar, means, yerr=stds, color=colors_line, capsize=5, ecolor=\"gray\"\n",
        ")\n",
        "ax2.set_ylabel(r\"Mean $\\langle Z_0 Z_i \\rangle$\", fontsize=11)\n",
        "ax2.set_title(\"Average fidelity\", fontsize=13, pad=12)\n",
        "y_range = max(means) - min(means) if max(means) != min(means) else 0.01\n",
        "# Top of ylim accounts for the bar height + std error bar + headroom for the value label\n",
        "y_top = max(m + s for m, s in zip(means, stds)) + y_range * 1.5\n",
        "ax2.set_ylim(min(means) - y_range * 0.8, y_top)\n",
        "for bar, val, std in zip(bars, means, stds):\n",
        "    ax2.text(\n",
        "        bar.get_x() + bar.get_width() / 2,\n",
        "        bar.get_height() + std + y_range * 0.15,\n",
        "        f\"{val:.4f}\",\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\",\n",
        "        fontsize=10,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "# Annotate % change vs pm_1\n",
        "baseline_mean = means[0]\n",
        "for i in range(1, len(means)):\n",
        "    pct = (means[i] - baseline_mean) / baseline_mean * 100\n",
        "    if abs(pct) > 0.01:\n",
        "        mid_y = (means[i] + ax2.get_ylim()[0]) / 2\n",
        "        ax2.text(\n",
        "            bars[i].get_x() + bars[i].get_width() / 2,\n",
        "            mid_y,\n",
        "            f\"{pct:+.1f}%\",\n",
        "            ha=\"center\",\n",
        "            va=\"center\",\n",
        "            fontsize=10,\n",
        "            color=\"white\",\n",
        "            fontweight=\"bold\",\n",
        "        )\n",
        "ax2.set_xticks(x_bar)\n",
        "ax2.set_xticklabels(names, fontsize=8, rotation=15)\n",
        "ax2.grid(axis=\"y\", linestyle=\"--\", alpha=0.5)\n",
        "\n",
        "fig.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "05235ab1-b473-4267-a34c-5f56e728550b",
      "metadata": {},
      "source": [
        "結果からは、トランスパイラによる変換の品質と実行の忠実度との間に明確な関連性が示されていますが、いくつかの留意点があります：\n",
        "\n",
        "* **`pm_1` (デフォルト)** : ベースライン。 SABREは試行回数がわずか20回、反復回数が4回にとどまるため、最適化の余地が限られており、その結果、SABREのみを用いた回路としては最も深い回路が得られた。\n",
        "* **`pm_2` （さらなる実験）** ：候補の数を10倍に増やして探索すると、レイアウトの深さはわずかに浅くなるが、この規模では深さの増加幅が小さいため、平均忠実度はほぼ横ばいとなる（ノイズの範囲内ではベースラインを下回ることもある）。\n",
        "* **`pm_3` （試行回数と反復回数の増加）** ：試行回数を2 `max_iterations` 倍の8回にすると、SABREの精緻化サイクルが増え、比較対象の中で最も単純なSABRE単独回路と、最も高い平均忠実度が得られる。\n",
        "* **`pm_star` (デフォルト + StarPreRouting )** : デフォルトのプリセットの初期化段階に を追加 `StarPreRouting` します。 `pm_3`構造を意識した書き換えにより、スター構造は線形チェーンに簡略化され、トランパイラの残りの部分がこれをデバイスの線形パスにマッピングすることで、全体として最も浅い回路（よりわずかに優れている）が生成され、誤差範囲内では と同等の忠実度を実現 `pm_3` している。 これは、SABREの確率的探索と比較すると、書き換え処理は実質的にコストがかからないため、デフォルト設定と同じトランスパイル時間で実行されます。\n",
        "\n",
        "なお、を増加 `max_iterations` させても、必ずしも良い結果が得られるとは限りません。 このケースでは大きな効果がありましたが、他の回路やバックエンドの場合、反復回数を増やしてもそれ以上の改善が見られないばかりか、局所的な最小値での過剰最適化により、パフォーマンスがわずかに低下する可能性もあります。 一般的に、時間的余裕が許す限り、と `swap_trials` をできるだけ増やすべきです `layout_trials` 。試行回数が多ければ多いほど、より良いレイアウトが見つかる可能性が高まるからです。 この増加 `max_iterations` についてはテストする価値がありますが、具体的な使用ケースに合わせて検証を行う必要があります。 のような `StarPreRouting` 特殊なパスは、その趣旨は似ているものの、回路に依存する度合いがより高い。つまり、対象とする構造が実際にその回路に含まれている場合にのみ有効である。 適用可能な場合はゲインが大きくなり、そうでない場合はゼロになりますが、試してみるのに実質的なコストはかかりません。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2b60e42-4aa2-4dc1-8a11-022072e79776",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## 大規模なハードウェアの例\n",
        "\n",
        "SABREでは、試験数の調整に加え、 **ルーティング**ヒューリスティックのカスタマイズもサポートしています。 SABREは3つのヒューリスティックを提供しています：\n",
        "\n",
        "* **`basic`**: 次のゲートまでの直線距離を最小化するスワップを選択する、単純な貪欲法。\n",
        "* **`decay`** (デフォルト): 直近のアクティビティに基づいて量子ビットに動的に重み付けを行い、同じ量子ビットでの繰り返しのスワップを抑制します。\n",
        "* **`lookahead`**: 今後のゲートを先読みして将来のルーティングコストを評価し、より良いスワップ順序を見出す可能性があります。\n",
        "\n",
        "カスタムヒューリスティックを使用するには、パスを作成し `SabreSwap` 、パラメータを `routing_pass` 介して に `SabreLayout` 接続します。\n",
        "\n",
        "`SabreLayout``pm_star_hw`比較対象として4つ目のパスマネージャーが追加されました。これはデフォルトの設定を維持しつつ、`SabreSwap` 初期化段階に を追加 `StarPreRouting` するものです。 この規模（100キュービット）では、SABRE検索はより困難になり、スター構造から線形チェーンへの書き換えが明らかに有利になります。これは、ヘロンプロセッサには、結果として得られる回路を収容するのに十分な長さの線形パスがあるからです。\n",
        "\n",
        "ここでは、3つのSABREヒューリスティックすべてを、 `StarPreRouting` 100キュービットのGHZ回路上で大規模に比較する。 SABRE構成について、異なるシード値を用いて複数のレイアウト試行を行い、それぞれから最適なトランスパイル済み回路を選択し、それらすべてを結果 `StarPreRouting` とともに実ハードウェアに送信します。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d755fafa-bbbc-4191-9ad9-a77f2af1bedc",
      "metadata": {},
      "source": [
        "<span id=\"steps-1-4-compressed-into-a-single-code-block\" />\n",
        "\n",
        "### 手順 1～4 を 1 つのコードブロックにまとめました\n",
        "\n",
        "ここでは、ワークフロー全体をより大きなスケールでまとめられています。 `SabreLayout`を `routing_pass` の として使用 `SabreSwap` する場合、1回の呼び出しにつき1回のレイアウト試行のみが行われるため、以下のコードセルでは、シードを順に試行してレイアウト空間を探索しています。\n",
        "\n",
        "（上記の）小規模なステップ2で定義されたのと同じ `wrap_sabre` ヘルパーを使用し、 \\[インデ]ックス1のステージも `routing` 同様にであるため、同様の `wrap_routing` ヘルパーを追加します。これをそのまま置き換えると、同様に保護バリアとゲーティングが `_swap_condition``ConditionalController([BarrierBeforeFinalMeasurements, routing_pass], ...)` 失われてしまうからです。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "4feb0fcf-d305-4741-82a7-b0fe0f0894fc",
      "metadata": {},
      "outputs": [],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "\n",
        "num_qubits = 100\n",
        "\n",
        "# Create star-topology GHZ circuit\n",
        "qc = QuantumCircuit(num_qubits)\n",
        "qc.h(0)\n",
        "for i in range(1, num_qubits):\n",
        "    qc.cx(0, i)\n",
        "qc.measure_all()\n",
        "\n",
        "# ZZ operators\n",
        "operator_strings = [\n",
        "    \"Z\" + \"I\" * i + \"Z\" + \"I\" * (num_qubits - 2 - i)\n",
        "    for i in range(num_qubits - 1)\n",
        "]\n",
        "operators = [SparsePauliOp(op) for op in operator_strings]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "43ec98c6-f4c6-4584-8ba0-edfb4e99f04a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "basic:\n",
            "  2Q depth: min: 524, mean: 570.5, std: 39.9\n",
            "  size    : min: 3819, mean: 4227.1, std: 360.6\n",
            "  best seed: 51 (2Q depth=524, size=3852)\n",
            "decay:\n",
            "  2Q depth: min: 387, mean: 436.4, std: 41.7\n",
            "  size    : min: 2687, mean: 3183.1, std: 459.3\n",
            "  best seed: 45 (2Q depth=387, size=2786)\n",
            "lookahead:\n",
            "  2Q depth: min: 364, mean: 424.6, std: 36.5\n",
            "  size    : min: 2335, mean: 3014.6, std: 388.1\n",
            "  best seed: 51 (2Q depth=364, size=2485)\n",
            "StarPreRouting:\n",
            "  2Q depth: min: 196, mean: 196.0, std: 0.0\n",
            "  size    : min: 1151, mean: 1151.0, std: 0.0\n",
            "  best seed: 42 (2Q depth=196, size=1151)\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 2-------------------------\n",
        "\n",
        "num_seeds = 10\n",
        "seed_list = [seed + i for i in range(num_seeds)]\n",
        "swap_trials = 200\n",
        "\n",
        "\n",
        "# The default routing[1] is a ConditionalController([barrier, routing_pass],\n",
        "# condition=_swap_condition); we re-wrap so the new routing pass keeps the\n",
        "# protective barrier and is skipped when routing isn't needed (matches the preset).\n",
        "def _swap_condition(property_set):\n",
        "    return not property_set[\"routing_not_needed\"]\n",
        "\n",
        "\n",
        "def wrap_routing(routing_pass):\n",
        "    return ConditionalController(\n",
        "        [\n",
        "            BarrierBeforeFinalMeasurements(\n",
        "                \"qiskit.transpiler.internal.routing.protection.barrier\"\n",
        "            ),\n",
        "            routing_pass,\n",
        "        ],\n",
        "        condition=_swap_condition,\n",
        "    )\n",
        "\n",
        "\n",
        "heuristic_results = {}\n",
        "\n",
        "# Three SABRE heuristics, swept over seeds\n",
        "for heuristic in [\"basic\", \"decay\", \"lookahead\"]:\n",
        "    trials = []\n",
        "    for s in seed_list:\n",
        "        sr = SabreSwap(\n",
        "            coupling_map=cmap, heuristic=heuristic, trials=swap_trials, seed=s\n",
        "        )\n",
        "        sl = SabreLayout(coupling_map=cmap, routing_pass=sr, seed=s)\n",
        "        pm = generate_preset_pass_manager(\n",
        "            optimization_level=3, backend=backend, seed_transpiler=s\n",
        "        )\n",
        "        # Re-wrap each custom pass in its original ConditionalController + barrier\n",
        "        # (wrap_sabre is defined in the small-scale Step 2 cell above).\n",
        "        pm.layout.replace(index=2, passes=wrap_sabre(sl))\n",
        "        pm.routing.replace(index=1, passes=wrap_routing(sr))\n",
        "\n",
        "        t0 = time.time()\n",
        "        tqc = pm.run(qc)\n",
        "        elapsed = time.time() - t0\n",
        "        depth = tqc.depth(lambda x: x.operation.num_qubits == 2)\n",
        "        size = tqc.size()\n",
        "        trials.append(\n",
        "            {\n",
        "                \"tqc\": tqc,\n",
        "                \"depth\": depth,\n",
        "                \"size\": size,\n",
        "                \"time\": elapsed,\n",
        "                \"seed\": s,\n",
        "            }\n",
        "        )\n",
        "\n",
        "    heuristic_results[heuristic] = trials\n",
        "\n",
        "# Default preset + StarPreRouting in init, also swept over seeds for a fair comparison\n",
        "star_trials = []\n",
        "for s in seed_list:\n",
        "    pm_star_hw = generate_preset_pass_manager(\n",
        "        optimization_level=3, backend=backend, seed_transpiler=s\n",
        "    )\n",
        "    pm_star_hw.init += StarPreRouting()\n",
        "\n",
        "    t0 = time.time()\n",
        "    tqc = pm_star_hw.run(qc)\n",
        "    elapsed = time.time() - t0\n",
        "    depth = tqc.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    size = tqc.size()\n",
        "    star_trials.append(\n",
        "        {\n",
        "            \"tqc\": tqc,\n",
        "            \"depth\": depth,\n",
        "            \"size\": size,\n",
        "            \"time\": elapsed,\n",
        "            \"seed\": s,\n",
        "        }\n",
        "    )\n",
        "heuristic_results[\"StarPreRouting\"] = star_trials\n",
        "\n",
        "# Print summary for each entry\n",
        "for label in [\"basic\", \"decay\", \"lookahead\", \"StarPreRouting\"]:\n",
        "    trials = heuristic_results[label]\n",
        "    depths = [t[\"depth\"] for t in trials]\n",
        "    sizes = [t[\"size\"] for t in trials]\n",
        "    best = min(trials, key=lambda t: t[\"depth\"])\n",
        "    print(f\"{label}:\")\n",
        "    print(\n",
        "        f\"  2Q depth: min: {min(depths)}, mean: {np.mean(depths):.1f}, std: {np.std(depths):.1f}\"\n",
        "    )\n",
        "    print(\n",
        "        f\"  size    : min: {min(sizes)}, mean: {np.mean(sizes):.1f}, std: {np.std(sizes):.1f}\"\n",
        "    )\n",
        "    print(\n",
        "        f\"  best seed: {best['seed']} (2Q depth={best['depth']}, size={best['size']})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "eead9bd2-17e0-4f5b-80bc-eb9b30af052e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/eead9bd2-17e0-4f5b-80bc-eb9b30af052e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "basic: best 2Q depth=524, size=3852 (seed=51)\n",
            "decay: best 2Q depth=387, size=2786 (seed=45)\n",
            "lookahead: best 2Q depth=364, size=2485 (seed=51)\n",
            "StarPreRouting: best 2Q depth=196, size=1151 (seed=42)\n"
          ]
        }
      ],
      "source": [
        "hw_colors = {\n",
        "    \"basic\": \"#ff7f0e\",\n",
        "    \"decay\": \"#d62728\",\n",
        "    \"lookahead\": \"#1f77b4\",\n",
        "    \"StarPreRouting\": \"#2a9d8f\",\n",
        "}\n",
        "\n",
        "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))\n",
        "\n",
        "for label in [\"basic\", \"decay\", \"lookahead\", \"StarPreRouting\"]:\n",
        "    trials = heuristic_results[label]\n",
        "    depths = [t[\"depth\"] for t in trials]\n",
        "    sizes = [t[\"size\"] for t in trials]\n",
        "    seeds = [t[\"seed\"] for t in trials]\n",
        "    color = hw_colors[label]\n",
        "\n",
        "    ax1.scatter(\n",
        "        seeds,\n",
        "        depths,\n",
        "        label=label,\n",
        "        color=color,\n",
        "        alpha=0.8,\n",
        "        edgecolor=\"k\",\n",
        "        s=60,\n",
        "    )\n",
        "    ax1.axhline(np.mean(depths), color=color, linestyle=\"--\", alpha=0.5)\n",
        "\n",
        "    ax2.scatter(\n",
        "        seeds,\n",
        "        sizes,\n",
        "        label=label,\n",
        "        color=color,\n",
        "        alpha=0.8,\n",
        "        edgecolor=\"k\",\n",
        "        s=60,\n",
        "    )\n",
        "    ax2.axhline(np.mean(sizes), color=color, linestyle=\"--\", alpha=0.5)\n",
        "\n",
        "ax1.set_xlabel(\"Seed\", fontsize=11)\n",
        "ax1.set_ylabel(\"2Q Depth\", fontsize=11)\n",
        "ax1.set_title(\"Two-Qubit Gate Depth per Seed\", fontsize=13)\n",
        "ax1.legend(fontsize=10)\n",
        "ax1.grid(alpha=0.3)\n",
        "\n",
        "ax2.set_xlabel(\"Seed\", fontsize=11)\n",
        "ax2.set_ylabel(\"Gate Count\", fontsize=11)\n",
        "ax2.set_title(\"Circuit Size per Seed\", fontsize=13)\n",
        "ax2.legend(fontsize=10)\n",
        "ax2.grid(alpha=0.3)\n",
        "\n",
        "plt.suptitle(\n",
        "    \"Transpilation variability across seeds: SABRE heuristics vs. StarPreRouting\",\n",
        "    fontsize=14,\n",
        "    fontweight=\"bold\",\n",
        "    y=1.02,\n",
        ")\n",
        "plt.tight_layout()\n",
        "plt.show()\n",
        "\n",
        "# Summary comparison\n",
        "for label in [\"basic\", \"decay\", \"lookahead\", \"StarPreRouting\"]:\n",
        "    best = min(heuristic_results[label], key=lambda t: t[\"depth\"])\n",
        "    print(\n",
        "        f\"{label}: best 2Q depth={best['depth']}, size={best['size']} (seed={best['seed']})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "5ad47245-41d0-4d90-ba94-dda4cd63705d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Best basic: 2Q depth=524, size=3852\n",
            "Best decay: 2Q depth=387, size=2786\n",
            "Best lookahead: 2Q depth=364, size=2485\n",
            "Best StarPreRouting: 2Q depth=196, size=1151\n",
            "basic job: d81q5tnoha1c73bknprg\n",
            "decay job: d81q5tugbeec73aktopg\n",
            "lookahead job: d81q5to0bvlc73d1epe0\n",
            "StarPreRouting job: d81q5u7tjchs73bn82hg\n",
            "basic job done\n",
            "decay job done\n",
            "lookahead job done\n",
            "StarPreRouting job done\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 3: Execute on hardware-------------------------\n",
        "\n",
        "best_circuits = {}\n",
        "for label in [\"basic\", \"decay\", \"lookahead\", \"StarPreRouting\"]:\n",
        "    best_circuits[label] = min(\n",
        "        heuristic_results[label], key=lambda t: t[\"depth\"]\n",
        "    )\n",
        "    b = best_circuits[label]\n",
        "    print(f\"Best {label}: 2Q depth={b['depth']}, size={b['size']}\")\n",
        "\n",
        "options = EstimatorOptions()\n",
        "options.resilience_level = 2\n",
        "options.dynamical_decoupling.enable = True\n",
        "options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "estimator = Estimator(backend, options=options)\n",
        "\n",
        "hw_jobs = {}\n",
        "hw_ops = {}\n",
        "for label, best in best_circuits.items():\n",
        "    hw_ops[label] = [op.apply_layout(best[\"tqc\"].layout) for op in operators]\n",
        "    hw_jobs[label] = estimator.run([(best[\"tqc\"], hw_ops[label])])\n",
        "    print(f\"{label} job: {hw_jobs[label].job_id()}\")\n",
        "estimator.options.environment.job_tags = [\"TUT_TOWS\"]\n",
        "\n",
        "hw_results = {}\n",
        "for label, job in hw_jobs.items():\n",
        "    hw_results[label] = job.result()[0]\n",
        "    print(f\"{label} job done\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "0280b0b9-6320-43e5-8396-f82f9e718319",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/transpilation-optimizations-with-sabre/extracted-outputs/0280b0b9-6320-43e5-8396-f82f9e718319-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Mean fidelity:\n",
            "  basic: 0.0344\n",
            "  decay: 0.1298\n",
            "  lookahead: 0.1857\n",
            "  StarPreRouting: 0.3295\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 4: Post-process-------------------------\n",
        "\n",
        "data = list(range(1, len(operators) + 1))\n",
        "hw_markers = {\n",
        "    \"basic\": \"D\",\n",
        "    \"decay\": \"o\",\n",
        "    \"lookahead\": \"s\",\n",
        "    \"StarPreRouting\": \"*\",\n",
        "}\n",
        "hw_labels = [\"basic\", \"decay\", \"lookahead\", \"StarPreRouting\"]\n",
        "\n",
        "fig, (ax1, ax2) = plt.subplots(\n",
        "    1, 2, figsize=(14, 5), gridspec_kw={\"width_ratios\": [2.5, 1]}\n",
        ")\n",
        "\n",
        "# Left: correlations vs distance\n",
        "for label in hw_labels:\n",
        "    evs = list(hw_results[label].data.evs)\n",
        "    b = best_circuits[label]\n",
        "    ax1.plot(\n",
        "        data,\n",
        "        evs,\n",
        "        marker=hw_markers[label],\n",
        "        color=hw_colors[label],\n",
        "        linewidth=2,\n",
        "        label=f\"{label} (2Q depth={b['depth']}, size={b['size']})\",\n",
        "        markersize=5 if label == \"StarPreRouting\" else 4,\n",
        "    )\n",
        "\n",
        "ax1.set_xlabel(\"Distance between qubits $i$\", fontsize=11)\n",
        "ax1.set_ylabel(r\"$\\langle Z_0 Z_i \\rangle$\", fontsize=11)\n",
        "ax1.set_title(\n",
        "    \"Entanglement correlations vs. qubit distance (hardware)\", fontsize=12\n",
        ")\n",
        "ax1.legend(fontsize=9)\n",
        "ax1.grid(alpha=0.3)\n",
        "\n",
        "# Right: mean fidelity bar chart\n",
        "hw_means = [np.mean(list(hw_results[label].data.evs)) for label in hw_labels]\n",
        "hw_bar_colors = [hw_colors[label] for label in hw_labels]\n",
        "x_bar = np.arange(len(hw_labels))\n",
        "bars = ax2.bar(x_bar, hw_means, color=hw_bar_colors)\n",
        "ax2.set_ylabel(r\"Mean $\\langle Z_0 Z_i \\rangle$\", fontsize=11)\n",
        "ax2.set_title(\"Average fidelity\", fontsize=13)\n",
        "y_range = (\n",
        "    max(hw_means) - min(hw_means) if max(hw_means) != min(hw_means) else 0.01\n",
        ")\n",
        "ax2.set_ylim(min(hw_means) - y_range * 0.2, max(hw_means) + y_range * 0.15)\n",
        "for bar, val in zip(bars, hw_means):\n",
        "    ax2.text(\n",
        "        bar.get_x() + bar.get_width() / 2,\n",
        "        bar.get_height() + y_range * 0.05,\n",
        "        f\"{val:.4f}\",\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\",\n",
        "        fontsize=11,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "ax2.set_xticks(x_bar)\n",
        "ax2.set_xticklabels(hw_labels, fontsize=9, rotation=15)\n",
        "ax2.grid(axis=\"y\", linestyle=\"--\", alpha=0.5)\n",
        "\n",
        "fig.tight_layout()\n",
        "plt.show()\n",
        "\n",
        "print(\"\\nMean fidelity:\")\n",
        "for label, m in zip(hw_labels, hw_means):\n",
        "    print(f\"  {label}: {m:.4f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "151e5fe9-872a-4b89-92f9-85bb883de17b",
      "metadata": {},
      "source": [
        "<span id=\"analysis\" />\n",
        "\n",
        "### 解析\n",
        "\n",
        "散布図からは、3つのSABREヒューリスティックすべてにおいて、種ごとに大きなばらつきが見られ、単一のトランスパイレーションに頼るのではなく、複数のレイアウト試行を実行することの重要性が浮き彫りになっている。 この `StarPreRouting` ラインは、シードを問わず実質的に横ばいとなっています。これは、構造が与えられれば、スター構造から線形チェーンへの書き換えが決定論的であるためです。また、下流のSABREルーティングでは線形チェーン上での自由度が極めて低いため、シードは最終的な深さやサイズにほとんど影響を与えません。\n",
        "\n",
        "トランスパイラの結果から、 および `lookahead` のヒューリスティックは `decay` 、いずれも を常に大幅な差で上回 `basic` っていることがわかる。 この `basic` ヒューリスティックは高速である一方、単純な貪欲法を採用しているため、回路が著しく複雑になることがよくあります。 `StarPreRouting`このスター型トポロジーのGHZ回路については、 `lookahead` その先見的なコスト関数が長距離の接続パターンを持つ回路に非常に適しているため、SABREヒューリスティックの中でも 2Q の深さとゲート数が最も小さくなる傾向がある。, しかし、この手法はこれら3つをすべて大幅に上回っている。ルーティングの前にスター構造を線形チェーンに書き換えることで、検索問題を完全に回避し、トランパイラの残りの部分が最小限の追加SWAPで線形パスにマッピングできる回路を生成する。\n",
        "\n",
        "その利点は、ハードウェアの忠実度にもそのまま反映されています。 2Q の深さやゲート数が少ないからといって、必ずしも忠実度が1対1で向上するわけではありません（レイアウトで使用される具体的な物理量子ビットや、実行時のキャリブレーションも重要だからです）。しかし、SABREとこ `StarPreRouting` ことの間の深さの差がこれほど大きい場合、構造を意識したアプローチが圧倒的に優れています。なぜなら、この回路ではデコヒーレンスの蓄積がはるかに少なく、2量子ビットエラーの発生もはるかに少ないからです。 忠実度の棒グラフを見ると `StarPreRouting` 、は最高のSABREヒューリスティックよりも大幅に上回っているのに対し `basic` 、は回路がはるかに複雑であるため、最も多くの誤差が蓄積され、他の手法を大きく下回っている。\n",
        "\n",
        "**主なポイント：**\n",
        "\n",
        "* SABREのヒューリスティック法のうち、 `decay` および `lookahead` は、非自明な回路において、よりも `basic` 著しく優れた性能を示す。 本番環境のワークロードには、この2つのうちいずれかを選択してください。\n",
        "* 最適なSABREヒューリスティックは、回路やハードウェアによって異なります。 複数のシードを用いて複数のヒューリスティックをテストすることが、最も信頼性の高い戦略である。\n",
        "* さらに多くのレイアウトを検討したい場合は、リモートノードに作業を分散させるのではなく、（および `layout_trials` 、カスタムルーティングパスをピン留めしていない場合は）を増やす `swap_trials` ようにしてください。 SABREパスでは、すでにローカルスレッド間でトライアルが並列化されており、トライアルごとの処理量はごくわずかであるため、通常、分散処理にかかるオーバーヘッドが速度向上効果を上回ってしまいます。\n",
        "* 回路に既知の特殊な構造がある場合、SABREの実行前に構造を意識したパスを適用することで `StarPreRouting` 、SABREのチューニングをいくら行っても達成できないほどの、桁違いの性能向上が得られる。 これはSABREの代替となるものではありません。 `StarPreRouting` 回路に実際にスター型サブ回路が含まれており、バックエンドに十分な長さの直線経路がある場合にのみ役立ちます。 回路の形状が分かっている場合は、passライブラリで一致するものが無いか確認してみる価値があります。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65c2cebe-50b9-4304-9123-bf4cea7ecff6",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 次のステップ\n",
        "\n",
        "この作品に興味を持たれた方は、以下の資料もご参照ください：\n",
        "\n",
        "<Admonition type=\"tip\" title=\"推奨事項\">\n",
        "  * [`SabreLayout` APIリファレンス](/docs/api/qiskit/qiskit.transpiler.passes.SabreLayout) ：パラメータに関する完全なドキュメント\n",
        "  * [SABRE論文](https://arxiv.org/abs/1809.02573) ：レイアウトおよび配線のためのオリジナルのSABREアルゴリズム\n",
        "  * [LightSABRE 論文](https://arxiv.org/abs/2409.08368) ：Qiskitの現在のSABRE実装を支えるアルゴリズムの改善点\n",
        "  * [カスタムトランスパイラーパスの作成](/docs/guides/custom-transpiler-pass) ：独自のトランスパイルロジックを構築する\n",
        "  * [トランスパイラープラグイン](/docs/guides/transpiler-plugins) ：サードパーティ製のパスを使用してQiskitのトランスパイレーションパイプラインを拡張する\n",
        "  * [DAGの表現](/docs/guides/DAG-representation) ：トランスパイラーが内部で使用する有向非循環グラフ（DAG）を理解する\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "42d1c053-1683-4c32-be1b-a36602207f74",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## チュートリアル調査\n",
        "\n",
        "このチュートリアルに関するご意見・ご感想をお寄せください。 あなたの洞察は、私たちのコンテンツの提供とユーザーエクスペリエンスを向上させるのに役立ちます。\n",
        "\n",
        "[アンケートへのリンク](https://your.feedback.ibm.com/jfe/form/SV_d9YWUSQIAvU9HXE)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1,
    "qpuSeconds": 60
  },
  "nbformat": 4,
  "nbformat_minor": 4
}