{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "a1b2c3d4",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Metodi di compilazione per circuiti di simulazione hamiltoniana\"\n",
        "description: \"Confronta i metodi di compilazione SABRE, il transpiler basato sull’intelligenza artificiale e Rustiq sui circuiti di simulazione hamiltoniani di Hamlib.\"\n",
        "---\n",
        "\n",
        "<span id=\"compilation-methods-for-hamiltonian-simulation-circuits\" />\n",
        "\n",
        "# Metodi di compilazione per circuiti di simulazione hamiltoniana\n",
        "\n",
        "*Stima del tempo di esecuzione: meno di 1 minuto su un processore Heron di IBM (NOTA: si tratta solo di una stima. (La durata potrebbe variare.)*\n",
        "\n",
        "{/* cspell:ignore Rustiq, nshuffles, edgecolors, edgecolor, Hamlib, Benchpress, Brugiere, Goubault, Martiel, Dubal, Lishman, Ivrii, fontweight, fontsize, textprops, wedgeprops, startangle, autopct, Hellinger, iloc, ylabel, frameon, ylims, CMHSC */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b1c2d3e4",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Dopo aver seguito questo tutorial, capirai:\n",
        "\n",
        "* Come utilizzare il transpiler Qiskit con SABRE per l'ottimizzazione del layout e del routing\n",
        "* Come sfruttare il transpiler basato sull'intelligenza artificiale per l'ottimizzazione avanzata dei circuiti\n",
        "* Come utilizzare il plugin Rustiq per sintetizzare `PauliEvolutionGate` le operazioni nei circuiti di simulazione hamiltoniana\n",
        "* Come valutare e confrontare i metodi di compilazione utilizzando la profondità a due qubit, il numero totale di gate e il tempo di esecuzione\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Ti consigliamo di acquisire familiarità con gli argomenti seguenti prima di seguire questo tutorial:\n",
        "\n",
        "* [Concetti di transpilazione](/docs/guides/transpile)\n",
        "* [Fasi del transpiler](/docs/guides/transpiler-stages)\n",
        "* [Transpilare con gestori di passaggi](/docs/guides/transpile-with-pass-managers)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1d2e3f4",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "La compilazione di circuiti quantistici trasforma un algoritmo quantistico di alto livello in un circuito fisico che rispetta i vincoli dell'hardware di destinazione. Una compilazione efficace può ridurre in modo significativo la profondità del circuito e il numero di gate, due fattori che incidono direttamente sulla qualità dei risultati ottenuti sui dispositivi quantistici di prossima generazione.\n",
        "\n",
        "Questo tutorial mette a confronto tre metodi di compilazione su circuiti di simulazione hamiltoniana realizzati con `PauliEvolutionGate`. Questi circuiti modellano le interazioni a coppie tra qubit (come i termini $ZZ$, $XX$ e $YY$ ) e sono comunemente utilizzati nella chimica quantistica, nella fisica della materia condensata e nella scienza dei materiali.\n",
        "\n",
        "I circuiti di riferimento provengono dalla raccolta [Hamlib](https://github.com/SRI-International/QC-App-Oriented-Benchmarks/tree/master/qedcbench/hamlib#hamlib-simulation---benchmark-program), accessibile tramite il repository [Benchpress](https://github.com/Qiskit/benchpress). Hamlib fornisce un insieme standardizzato di hamiltoniani rappresentativi, consentendo di confrontare le strategie di compilazione su carichi di lavoro di simulazione realistici.\n",
        "\n",
        "<span id=\"compilation-methods-overview\" />\n",
        "\n",
        "### Panoramica dei metodi di compilazione\n",
        "\n",
        "<span id=\"qiskit-transpiler-with-sabre\" />\n",
        "\n",
        "#### Transpiler Qiskit con SABRE\n",
        "\n",
        "Il transpiler Qiskit utilizza l'algoritmo SABRE ( BidiREctional, ricerca euristica basata su SWAP) per ottimizzare il layout e il routing dei circuiti. SABRE punta a ridurre al minimo i gate SWAP e il loro impatto sulla profondità del circuito, nel rispetto dei vincoli di connettività dell'hardware. Si tratta di un metodo di uso generale che offre un buon equilibrio tra prestazioni e tempo di compilazione. Per ulteriori dettagli, si veda [\\[1\\]](https://arxiv.org/abs/2409.08368). I vantaggi e l'analisi dei parametri di SABRE sono trattati in modo approfondito in un [tutorial](/docs/tutorials/transpilation-optimizations-with-sabre) a parte.\n",
        "\n",
        "<span id=\"ai-powered-transpiler\" />\n",
        "\n",
        "#### Transpiler basato sull'intelligenza artificiale\n",
        "\n",
        "Il transpiler basato sull'intelligenza artificiale utilizza l'apprendimento automatico per prevedere le strategie di transpilazione ottimali, analizzando i modelli presenti nella struttura dei circuiti e i vincoli hardware. Può inoltre applicare il metodo `AIPauliNetworkSynthesis` \"pass\", che prende di mira i circuiti della rete di Pauli utilizzando un approccio di sintesi basato sull'apprendimento per rinforzo. Per ulteriori informazioni, si vedano [\\[2\\]](https://arxiv.org/abs/2405.13196) e [\\[3\\]](https://arxiv.org/abs/2503.14448).\n",
        "\n",
        "<span id=\"rustiq-plugin\" />\n",
        "\n",
        "#### Plugin Rustiq\n",
        "\n",
        "Il plugin Rustiq offre tecniche di sintesi avanzate specificamente pensate per `PauliEvolutionGate` le operazioni che rappresentano le rotazioni di Pauli comunemente utilizzate nella dinamica trotterizzata. È progettato per generare scomposizioni di circuiti a bassa profondità per carichi di lavoro di simulazione hamiltoniana. Per ulteriori dettagli, si veda [\\[4\\]](https://arxiv.org/abs/2404.03280).\n",
        "\n",
        "<span id=\"key-metrics\" />\n",
        "\n",
        "### Metriche chiave\n",
        "\n",
        "Confrontiamo i tre metodi sulla base dei seguenti parametri:\n",
        "\n",
        "* **Profondità a due qubit** : la profondità del circuito che tiene conto solo delle porte a due qubit. Questo rappresenta spesso il collo di bottiglia per la fedeltà su hardware reale.\n",
        "* **Dimensioni del circuito (numero totale di porte logiche)** : il numero totale di porte logiche presenti nel circuito transpilato.\n",
        "* **Durata** : il tempo effettivo trascorso per la transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d1e2f3a4",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Prima di iniziare questa esercitazione, assicuratevi di aver installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.0 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.22 o versioni successive (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "* Qiskit IBM Transpiler (`pip install qiskit-ibm-transpiler`)\n",
        "* Qiskit AI Transpiler in modalità locale (`pip install qiskit_ibm_ai_local_transpiler`)\n",
        "* Networkx (`pip install networkx`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e1f2a3b4",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "f1a2b3c4",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import QuantumCircuit\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "from qiskit.circuit.library import PauliEvolutionGate\n",
        "from qiskit_ibm_transpiler import generate_ai_pass_manager\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel, depolarizing_error\n",
        "from collections import Counter\n",
        "from statistics import mean, stdev\n",
        "from scipy.sparse import SparseEfficiencyWarning\n",
        "import time\n",
        "import warnings\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib.ticker as ticker\n",
        "import numpy as np\n",
        "import json\n",
        "import requests\n",
        "import logging\n",
        "\n",
        "# Suppress noisy loggers and warnings\n",
        "logging.getLogger(\n",
        "    \"qiskit_ibm_transpiler.wrappers.ai_local_synthesis\"\n",
        ").setLevel(logging.ERROR)\n",
        "warnings.filterwarnings(\"ignore\", category=FutureWarning)\n",
        "warnings.filterwarnings(\"ignore\", category=SparseEfficiencyWarning)\n",
        "\n",
        "seed = 42  # Seed for reproducibility"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2b3c4d5",
      "metadata": {},
      "source": [
        "<span id=\"connect-to-a-backend\" />\n",
        "\n",
        "### Connettiti a un backend\n",
        "\n",
        "Seleziona un backend da utilizzare sia per gli esempi su piccola scala che per quelli su larga scala. Il backend determina la mappa di accoppiamento e i gate di base a cui il transpiler si rivolge.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b2c3d4e5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using backend: ibm_pittsburgh\n"
          ]
        }
      ],
      "source": [
        "# QiskitRuntimeService.save_account(channel=\"ibm_quantum_platform\",\n",
        "# token=\"<YOUR-API-KEY>\", overwrite=True, set_as_default=True)\n",
        "service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "print(f\"Using backend: {backend.name}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2d3e4f5",
      "metadata": {},
      "source": [
        "<span id=\"define-pass-managers\" />\n",
        "\n",
        "### Definire i gestori dei pass\n",
        "\n",
        "Configurare i tre metodi di compilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "d2e3f4a5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# SABRE pass manager (Qiskit default at optimization level 3)\n",
        "pm_sabre = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=seed\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "e2f3a4b5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "e14d6bb64d3e4a399a645a5437a4a1cb",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Fetching 127 files:   0%|          | 0/127 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# AI transpiler pass manager (local mode)\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    backend=backend, optimization_level=3, ai_optimization_level=3\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "f2a3b4c5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Rustiq pass manager for PauliEvolutionGate synthesis\n",
        "hls_config = HLSConfig(\n",
        "    PauliEvolution=[\n",
        "        (\n",
        "            \"rustiq\",\n",
        "            {\n",
        "                \"nshuffles\": 400,\n",
        "                \"upto_phase\": True,\n",
        "                \"fix_clifford\": True,\n",
        "                \"preserve_order\": False,\n",
        "                \"metric\": \"depth\",\n",
        "            },\n",
        "        )\n",
        "    ]\n",
        ")\n",
        "pm_rustiq = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    hls_config=hls_config,\n",
        "    seed_transpiler=seed,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3b4c5d6",
      "metadata": {},
      "source": [
        "<span id=\"define-helper-functions\" />\n",
        "\n",
        "### Definire le funzioni di supporto\n",
        "\n",
        "La seguente funzione esegue la transpilazione di un elenco di circuiti utilizzando un determinato gestore di passaggi e registra le metriche chiave (profondità a due qubit, dimensione del circuito e tempo di esecuzione) per ciascun circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "b3c4d5e6",
      "metadata": {},
      "outputs": [],
      "source": [
        "def capture_transpilation_metrics(\n",
        "    results, pass_manager, circuits, method_name\n",
        "):\n",
        "    \"\"\"\n",
        "    Transpile circuits and append one metrics record per circuit to\n",
        "    ``results``.\n",
        "\n",
        "    Args:\n",
        "        results (list): List of dicts to append the metrics records to.\n",
        "        pass_manager: Pass manager used for transpilation.\n",
        "        circuits (list): List of quantum circuits to transpile.\n",
        "        method_name (str): Name of the transpilation method.\n",
        "\n",
        "    Returns:\n",
        "        list: List of transpiled circuits.\n",
        "    \"\"\"\n",
        "    transpiled_circuits = []\n",
        "\n",
        "    for i, qc in enumerate(circuits):\n",
        "        start_time = time.time()\n",
        "        transpiled_qc = pass_manager.run(qc)\n",
        "        end_time = time.time()\n",
        "\n",
        "        # Decompose swaps for consistency across methods\n",
        "        transpiled_qc = transpiled_qc.decompose(gates_to_decompose=[\"swap\"])\n",
        "\n",
        "        transpilation_time = end_time - start_time\n",
        "        two_qubit_depth = transpiled_qc.depth(\n",
        "            lambda x: x.operation.num_qubits == 2\n",
        "        )\n",
        "        circuit_size = transpiled_qc.size()\n",
        "\n",
        "        results.append(\n",
        "            {\n",
        "                \"method\": method_name,\n",
        "                \"qc_name\": qc.name,\n",
        "                \"qc_index\": i,\n",
        "                \"num_qubits\": qc.num_qubits,\n",
        "                \"two_qubit_depth\": two_qubit_depth,\n",
        "                \"size\": circuit_size,\n",
        "                \"runtime\": transpilation_time,\n",
        "            }\n",
        "        )\n",
        "        transpiled_circuits.append(transpiled_qc)\n",
        "        print(\n",
        "            f\"[{method_name}] Circuit {i} ({qc.name}): \"\n",
        "            f\"2Q depth={two_qubit_depth}, size={circuit_size}, \"\n",
        "            f\"time={transpilation_time:.2f}s\"\n",
        "        )\n",
        "\n",
        "    return transpiled_circuits"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "summary_table_fn",
      "metadata": {},
      "outputs": [],
      "source": [
        "def _method_order(results):\n",
        "    \"\"\"Return the distinct method names in their first-seen order.\"\"\"\n",
        "    order = []\n",
        "    for r in results:\n",
        "        if r[\"method\"] not in order:\n",
        "            order.append(r[\"method\"])\n",
        "    return order\n",
        "\n",
        "\n",
        "def print_summary_table(results):\n",
        "    \"\"\"\n",
        "    Print the mean and standard deviation of each metric per compilation\n",
        "    method, followed by the mean percent improvement relative to SABRE.\n",
        "    \"\"\"\n",
        "    metrics = [\n",
        "        (\"two_qubit_depth\", \"2Q Depth\"),\n",
        "        (\"size\", \"Gate Count\"),\n",
        "        (\"runtime\", \"Runtime (s)\"),\n",
        "    ]\n",
        "    methods = _method_order(results)\n",
        "    by_method = {m: [r for r in results if r[\"method\"] == m] for m in methods}\n",
        "    sabre_by_index = {r[\"qc_index\"]: r for r in by_method.get(\"SABRE\", [])}\n",
        "\n",
        "    col_w = 22\n",
        "    name_w = max(len(m) for m in methods)\n",
        "    header = f\"{'Method':<{name_w}}\" + \"\".join(\n",
        "        f\"  {label:>{col_w}}\" for _, label in metrics\n",
        "    )\n",
        "\n",
        "    print(\"Mean +/- std per compilation method\")\n",
        "    print(header)\n",
        "    print(\"-\" * len(header))\n",
        "    for method in methods:\n",
        "        cells = []\n",
        "        for key, _ in metrics:\n",
        "            values = [r[key] for r in by_method[method]]\n",
        "            std = stdev(values) if len(values) > 1 else 0.0\n",
        "            cells.append(f\"{mean(values):,.1f} +/- {std:,.1f}\")\n",
        "        print(\n",
        "            f\"{method:<{name_w}}\" + \"\".join(f\"  {c:>{col_w}}\" for c in cells)\n",
        "        )\n",
        "\n",
        "    others = [m for m in methods if m != \"SABRE\"]\n",
        "    if others and sabre_by_index:\n",
        "        print()\n",
        "        print(\"Mean % improvement vs SABRE (positive = better than SABRE)\")\n",
        "        print(header)\n",
        "        print(\"-\" * len(header))\n",
        "        for method in others:\n",
        "            cells = []\n",
        "            for key, _ in metrics:\n",
        "                pct = [\n",
        "                    (sabre_by_index[r[\"qc_index\"]][key] - r[key])\n",
        "                    / sabre_by_index[r[\"qc_index\"]][key]\n",
        "                    * 100\n",
        "                    for r in by_method[method]\n",
        "                    if sabre_by_index.get(r[\"qc_index\"])\n",
        "                    and sabre_by_index[r[\"qc_index\"]][key]\n",
        "                ]\n",
        "                if pct:\n",
        "                    std = stdev(pct) if len(pct) > 1 else 0.0\n",
        "                    cells.append(f\"{mean(pct):+.1f}% +/- {std:.1f}%\")\n",
        "                else:\n",
        "                    cells.append(\"n/a\")\n",
        "            print(\n",
        "                f\"{method:<{name_w}}\"\n",
        "                + \"\".join(f\"  {c:>{col_w}}\" for c in cells)\n",
        "            )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "per_circuit_table_fn",
      "metadata": {},
      "outputs": [],
      "source": [
        "def print_per_circuit_comparison(results, num_rows=5):\n",
        "    \"\"\"\n",
        "    Print a per-metric comparison of the compilation methods for the\n",
        "    first ``num_rows`` circuits (sorted by qubit count). The best\n",
        "    (lowest) value for each metric is marked with an asterisk.\n",
        "    \"\"\"\n",
        "    metrics = [\n",
        "        (\"two_qubit_depth\", \"2Q Depth\"),\n",
        "        (\"size\", \"Gate Count\"),\n",
        "        (\"runtime\", \"Runtime (s)\"),\n",
        "    ]\n",
        "    methods = _method_order(results)\n",
        "\n",
        "    by_index = {}\n",
        "    for r in results:\n",
        "        by_index.setdefault(r[\"qc_index\"], {})[r[\"method\"]] = r\n",
        "    ordered = sorted(\n",
        "        by_index.items(),\n",
        "        key=lambda kv: (next(iter(kv[1].values()))[\"num_qubits\"], kv[0]),\n",
        "    )[:num_rows]\n",
        "\n",
        "    for key, label in metrics:\n",
        "        print(f\"{label} (first {num_rows} circuits by qubit count); * = best\")\n",
        "        header = f\"{'Idx':>3}  {'Circuit':<16} {'Q':>3}\" + \"\".join(\n",
        "            f\"{m:>9}\" for m in methods\n",
        "        )\n",
        "        print(header)\n",
        "        print(\"-\" * len(header))\n",
        "        for idx, method_map in ordered:\n",
        "            any_record = next(iter(method_map.values()))\n",
        "            present = {\n",
        "                m: method_map[m][key] for m in methods if m in method_map\n",
        "            }\n",
        "            best = min(present.values())\n",
        "            line = (\n",
        "                f\"{idx:>3}  {any_record['qc_name'][:16]:<16} \"\n",
        "                f\"{any_record['num_qubits']:>3}\"\n",
        "            )\n",
        "            for m in methods:\n",
        "                value = method_map[m][key]\n",
        "                text = f\"{value:.2f}\" if key == \"runtime\" else f\"{int(value)}\"\n",
        "                if value == best:\n",
        "                    text += \"*\"\n",
        "                line += f\"{text:>9}\"\n",
        "            print(line)\n",
        "        print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3d4e5f6",
      "metadata": {},
      "source": [
        "<span id=\"load-hamiltonian-circuits-from-hamlib\" />\n",
        "\n",
        "### Carica i circuiti hamiltoniani da Hamlib\n",
        "\n",
        "Carichiamo un insieme rappresentativo di hamiltoniani dal repository Benchpress e costruiamo `PauliEvolutionGate` dei circuiti. I circuiti che superano il numero di qubit del backend vengono rimossi, così come quelli la cui dimensione, una volta scomposta, supera i 1.500 gate (per mantenere i tempi di transpilazione entro limiti ragionevoli).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "d3e4f5a6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total Hamiltonian circuits loaded: 42\n",
            "Qubit range: 2 to 112\n"
          ]
        }
      ],
      "source": [
        "# Obtain the Hamiltonian JSON from the benchpress repository\n",
        "url = (\n",
        "    \"https://raw.githubusercontent.com/Qiskit/benchpress/\"\n",
        "    \"e7b29ef7be4cc0d70237b8fdc03edbd698908eff/\"\n",
        "    \"benchpress/hamiltonian/hamlib/100_representative.json\"\n",
        ")\n",
        "response = requests.get(url)\n",
        "response.raise_for_status()\n",
        "ham_records = json.loads(response.text)\n",
        "\n",
        "# Remove circuits that are too large for the backend\n",
        "ham_records = [\n",
        "    h for h in ham_records if h[\"ham_qubits\"] <= backend.num_qubits\n",
        "]\n",
        "\n",
        "# Build PauliEvolutionGate circuits\n",
        "qc_ham_list = []\n",
        "for h in ham_records:\n",
        "    terms = h[\"ham_hamlib_hamiltonian_terms\"]\n",
        "    coeff = h[\"ham_hamlib_hamiltonian_coefficients\"]\n",
        "    num_qubits = h[\"ham_qubits\"]\n",
        "    name = h[\"ham_problem\"]\n",
        "\n",
        "    evo_gate = PauliEvolutionGate(SparsePauliOp(terms, coeff))\n",
        "    qc = QuantumCircuit(num_qubits)\n",
        "    qc.name = name\n",
        "    qc.append(evo_gate, range(num_qubits))\n",
        "    qc_ham_list.append(qc)\n",
        "\n",
        "# Remove circuits whose decomposed size exceeds 1500 gates so that\n",
        "# transpilation completes in a reasonable time frame\n",
        "qc_ham_list = [qc for qc in qc_ham_list if qc.decompose().size() <= 1500]\n",
        "\n",
        "print(f\"Total Hamiltonian circuits loaded: {len(qc_ham_list)}\")\n",
        "min_qubits = min(qc.num_qubits for qc in qc_ham_list)\n",
        "max_qubits = max(qc.num_qubits for qc in qc_ham_list)\n",
        "print(f\"Qubit range: {min_qubits} to {max_qubits}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e3f4a5b6",
      "metadata": {},
      "source": [
        "Suddividere i circuiti in gruppi su piccola scala (meno di 20 qubit) e su larga scala (20 o più qubit).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "f3a4b5c6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Small-scale circuits (<20 qubits): 20\n",
            "Large-scale circuits (>=20 qubits): 22\n"
          ]
        }
      ],
      "source": [
        "qc_small = [qc for qc in qc_ham_list if qc.num_qubits < 20]\n",
        "qc_large = [qc for qc in qc_ham_list if qc.num_qubits >= 20]\n",
        "\n",
        "print(f\"Small-scale circuits (<20 qubits): {len(qc_small)}\")\n",
        "print(f\"Large-scale circuits (>=20 qubits): {len(qc_large)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4b5c6d7",
      "metadata": {},
      "source": [
        "Visualizza in anteprima uno dei circuiti hamiltoniani su piccola scala prima della transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "b4c5d6e7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/b4c5d6e7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We decompose the circuit here, otherwise it would just be a PauliEvolutionGate box,\n",
        "# which isn't very informative to look at!\n",
        "qc_small[0].decompose().draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sec-small-scale",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-example\" />\n",
        "\n",
        "## Esempio su piccola scala\n",
        "\n",
        "In questa sezione, mettiamo a confronto i tre metodi di compilazione su circuiti hamiltoniani con meno di 20 qubit. Questi circuiti vengono compilati rapidamente e offrono una chiara visione di come ciascun metodo gestisca circuiti di moderata complessità.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4d5e6f7",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Fase 1: mappare gli input classici su un problema quantistico\n",
        "\n",
        "Ogni hamiltoniano è codificato come un `PauliEvolutionGate` circuito. I circuiti erano già stati realizzati nella sezione \"Setup\" sulla base dei dati del benchmark di Hamlib.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4e5f6a7",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Fase 2: Ottimizzazione del problema per l'esecuzione su hardware quantistico\n",
        "\n",
        "Effettuiamo la transpilazione di tutti i circuiti su piccola scala utilizzando ciascuno dei tre gestori di passaggi, quindi raccogliamo le metriche.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "e4f5a6b7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[SABRE] Circuit 0 (all-vib-bh): 2Q depth=3, size=30, time=2.09s\n",
            "[SABRE] Circuit 1 (all-vib-c2h): 2Q depth=18, size=111, time=0.01s\n",
            "[SABRE] Circuit 2 (all-vib-o3): 2Q depth=6, size=58, time=0.00s\n",
            "[SABRE] Circuit 3 (all-vib-c2h): 2Q depth=2, size=37, time=0.01s\n",
            "[SABRE] Circuit 4 (graph-gnp_k-2): 2Q depth=24, size=126, time=0.01s\n",
            "[SABRE] Circuit 5 (LiH): 2Q depth=66, size=285, time=0.01s\n",
            "[SABRE] Circuit 6 (all-vib-fccf): 2Q depth=66, size=339, time=0.01s\n",
            "[SABRE] Circuit 7 (all-vib-ch2): 2Q depth=88, size=413, time=0.01s\n",
            "[SABRE] Circuit 8 (all-vib-f2): 2Q depth=180, size=1000, time=0.02s\n",
            "[SABRE] Circuit 9 (all-vib-bhf2): 2Q depth=18, size=223, time=0.03s\n",
            "[SABRE] Circuit 10 (graph-gnp_k-4): 2Q depth=122, size=675, time=0.02s\n",
            "[SABRE] Circuit 11 (Be2): 2Q depth=343, size=1628, time=0.03s\n",
            "[SABRE] Circuit 12 (all-vib-fccf): 2Q depth=14, size=134, time=0.00s\n",
            "[SABRE] Circuit 13 (uf20-ham): 2Q depth=50, size=341, time=0.01s\n",
            "[SABRE] Circuit 14 (TSP_Ncity-4): 2Q depth=118, size=615, time=0.01s\n",
            "[SABRE] Circuit 15 (graph-complete_bipart): 2Q depth=232, size=1420, time=0.03s\n",
            "[SABRE] Circuit 16 (all-vib-cyclo_propene): 2Q depth=18, size=354, time=0.93s\n",
            "[SABRE] Circuit 17 (all-vib-hno): 2Q depth=6, size=174, time=0.14s\n",
            "[SABRE] Circuit 18 (all-vib-fccf): 2Q depth=30, size=286, time=0.01s\n",
            "[SABRE] Circuit 19 (tfim): 2Q depth=31, size=232, time=0.03s\n",
            "[AI] Circuit 0 (all-vib-bh): 2Q depth=3, size=30, time=0.01s\n"
          ]
        },
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "cf50b939bf4141bcbc610275a514d6fd",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Fetching 4 files:   0%|          | 0/4 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[AI] Circuit 1 (all-vib-c2h): 2Q depth=18, size=101, time=0.18s\n",
            "[AI] Circuit 2 (all-vib-o3): 2Q depth=6, size=58, time=0.01s\n",
            "[AI] Circuit 3 (all-vib-c2h): 2Q depth=2, size=37, time=0.01s\n",
            "[AI] Circuit 4 (graph-gnp_k-2): 2Q depth=24, size=133, time=0.07s\n",
            "[AI] Circuit 5 (LiH): 2Q depth=62, size=267, time=8.00s\n",
            "[AI] Circuit 6 (all-vib-fccf): 2Q depth=65, size=300, time=0.18s\n",
            "[AI] Circuit 7 (all-vib-ch2): 2Q depth=79, size=353, time=0.16s\n",
            "[AI] Circuit 8 (all-vib-f2): 2Q depth=176, size=998, time=0.43s\n",
            "[AI] Circuit 9 (all-vib-bhf2): 2Q depth=18, size=194, time=0.11s\n",
            "[AI] Circuit 10 (graph-gnp_k-4): 2Q depth=114, size=668, time=0.18s\n",
            "[AI] Circuit 11 (Be2): 2Q depth=292, size=1382, time=0.88s\n",
            "[AI] Circuit 12 (all-vib-fccf): 2Q depth=14, size=134, time=0.01s\n",
            "[AI] Circuit 13 (uf20-ham): 2Q depth=40, size=330, time=0.16s\n",
            "[AI] Circuit 14 (TSP_Ncity-4): 2Q depth=96, size=600, time=0.29s\n",
            "[AI] Circuit 15 (graph-complete_bipart): 2Q depth=231, size=1531, time=0.46s\n",
            "[AI] Circuit 16 (all-vib-cyclo_propene): 2Q depth=18, size=309, time=0.25s\n",
            "[AI] Circuit 17 (all-vib-hno): 2Q depth=10, size=198, time=0.15s\n",
            "[AI] Circuit 18 (all-vib-fccf): 2Q depth=34, size=402, time=0.02s\n",
            "[AI] Circuit 19 (tfim): 2Q depth=44, size=311, time=0.15s\n",
            "[Rustiq] Circuit 0 (all-vib-bh): 2Q depth=3, size=30, time=0.01s\n",
            "[Rustiq] Circuit 1 (all-vib-c2h): 2Q depth=13, size=69, time=0.00s\n",
            "[Rustiq] Circuit 2 (all-vib-o3): 2Q depth=13, size=82, time=0.01s\n",
            "[Rustiq] Circuit 3 (all-vib-c2h): 2Q depth=2, size=40, time=0.01s\n",
            "[Rustiq] Circuit 4 (graph-gnp_k-2): 2Q depth=31, size=132, time=0.01s\n",
            "[Rustiq] Circuit 5 (LiH): 2Q depth=59, size=285, time=0.01s\n",
            "[Rustiq] Circuit 6 (all-vib-fccf): 2Q depth=34, size=193, time=0.00s\n",
            "[Rustiq] Circuit 7 (all-vib-ch2): 2Q depth=49, size=302, time=0.01s\n",
            "[Rustiq] Circuit 8 (all-vib-f2): 2Q depth=141, size=807, time=0.02s\n",
            "[Rustiq] Circuit 9 (all-vib-bhf2): 2Q depth=13, size=146, time=0.02s\n",
            "[Rustiq] Circuit 10 (graph-gnp_k-4): 2Q depth=129, size=683, time=0.02s\n",
            "[Rustiq] Circuit 11 (Be2): 2Q depth=220, size=1101, time=0.02s\n",
            "[Rustiq] Circuit 12 (all-vib-fccf): 2Q depth=53, size=333, time=0.01s\n",
            "[Rustiq] Circuit 13 (uf20-ham): 2Q depth=63, size=425, time=0.01s\n",
            "[Rustiq] Circuit 14 (TSP_Ncity-4): 2Q depth=123, size=767, time=0.02s\n",
            "[Rustiq] Circuit 15 (graph-complete_bipart): 2Q depth=309, size=2107, time=0.05s\n",
            "[Rustiq] Circuit 16 (all-vib-cyclo_propene): 2Q depth=16, size=283, time=0.32s\n",
            "[Rustiq] Circuit 17 (all-vib-hno): 2Q depth=19, size=291, time=0.32s\n",
            "[Rustiq] Circuit 18 (all-vib-fccf): 2Q depth=44, size=546, time=0.02s\n",
            "[Rustiq] Circuit 19 (tfim): 2Q depth=24, size=416, time=0.01s\n"
          ]
        }
      ],
      "source": [
        "results_small = []\n",
        "\n",
        "tqc_sabre_small = capture_transpilation_metrics(\n",
        "    results_small, pm_sabre, qc_small, \"SABRE\"\n",
        ")\n",
        "tqc_ai_small = capture_transpilation_metrics(\n",
        "    results_small, pm_ai, qc_small, \"AI\"\n",
        ")\n",
        "tqc_rustiq_small = capture_transpilation_metrics(\n",
        "    results_small, pm_rustiq, qc_small, \"Rustiq\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f4a5b6c7",
      "metadata": {},
      "source": [
        "La tabella che segue riassume la media e la deviazione standard di ciascun parametro per tutti i circuiti su piccola scala, insieme alla percentuale di miglioramento rispetto a SABRE. Poiché le dimensioni dei circuiti variano notevolmente, la deviazione standard fornisce un contesto importante per l'interpretazione delle medie.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "a5b6c7d8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mean +/- std per compilation method\n",
            "Method                2Q Depth              Gate Count             Runtime (s)\n",
            "------------------------------------------------------------------------------\n",
            "SABRE            71.8 +/- 89.6         424.1 +/- 446.0             0.2 +/- 0.5\n",
            "AI               67.3 +/- 80.2         416.8 +/- 426.7             0.6 +/- 1.8\n",
            "Rustiq           67.9 +/- 80.0         451.9 +/- 484.7             0.0 +/- 0.1\n",
            "\n",
            "Mean % improvement vs SABRE (positive = better than SABRE)\n",
            "Method                2Q Depth              Gate Count             Runtime (s)\n",
            "------------------------------------------------------------------------------\n",
            "AI             -2.1% +/- 19.8%         -0.6% +/- 14.7%   -5635.1% +/- 20725.2%\n",
            "Rustiq        -25.3% +/- 85.4%        -16.3% +/- 50.4%         -7.0% +/- 60.6%\n"
          ]
        }
      ],
      "source": [
        "print_summary_table(results_small)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "per_circuit_small_md",
      "metadata": {},
      "source": [
        "La tabella relativa ai singoli circuiti mostra il confronto tra i vari metodi sui singoli circuiti. Il valore migliore per ciascun indicatore è contrassegnato da un asterisco. Si noti che, nei circuiti più semplici, tutti e tre i metodi spesso convergono verso lo stesso risultato.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "per_circuit_small_code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2Q Depth (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-bh         2       3*       3*       3*\n",
            "  1  all-vib-c2h        3       18       18      13*\n",
            "  2  all-vib-o3         4       6*       6*       13\n",
            "  3  all-vib-c2h        4       2*       2*       2*\n",
            "  4  graph-gnp_k-2      4      24*      24*       31\n",
            "  5  LiH                4       66       62      59*\n",
            "  6  all-vib-fccf       4       66       65      34*\n",
            "  7  all-vib-ch2        4       88       79      49*\n",
            "\n",
            "Gate Count (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-bh         2      30*      30*      30*\n",
            "  1  all-vib-c2h        3      111      101      69*\n",
            "  2  all-vib-o3         4      58*      58*       82\n",
            "  3  all-vib-c2h        4      37*      37*       40\n",
            "  4  graph-gnp_k-2      4     126*      133      132\n",
            "  5  LiH                4      285     267*      285\n",
            "  6  all-vib-fccf       4      339      300     193*\n",
            "  7  all-vib-ch2        4      413      353     302*\n",
            "\n",
            "Runtime (s) (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-bh         2     2.09     0.01    0.01*\n",
            "  1  all-vib-c2h        3     0.01     0.18    0.00*\n",
            "  2  all-vib-o3         4    0.00*     0.01     0.01\n",
            "  3  all-vib-c2h        4     0.01     0.01    0.01*\n",
            "  4  graph-gnp_k-2      4    0.01*     0.07     0.01\n",
            "  5  LiH                4    0.01*     8.00     0.01\n",
            "  6  all-vib-fccf       4     0.01     0.18    0.00*\n",
            "  7  all-vib-ch2        4     0.01     0.16    0.01*\n",
            "\n"
          ]
        }
      ],
      "source": [
        "print_per_circuit_comparison(results_small, num_rows=8)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b5c6d7e8",
      "metadata": {},
      "source": [
        "<span id=\"visualize-results\" />\n",
        "\n",
        "#### Visualizza i risultati\n",
        "\n",
        "I grafici riportati di seguito mettono a confronto i tre metodi in base a ciascun indicatore, circuito per circuito. I circuiti sono ordinati in base al numero di qubit e contrassegnati da un indice sull'asse x, poiché più circuiti possono condividere lo stesso numero di qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "c5d6e7f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_transpilation_comparison(results, title_prefix):\n",
        "    \"\"\"\n",
        "    Create a three-panel figure comparing compilation methods on\n",
        "    two-qubit depth, circuit size, and runtime.\n",
        "\n",
        "    Circuits are sorted by qubit count and plotted by circuit index.\n",
        "    \"\"\"\n",
        "    methods = _method_order(results)\n",
        "    palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n",
        "    markers = {\"SABRE\": \"o\", \"AI\": \"^\", \"Rustiq\": \"s\"}\n",
        "\n",
        "    # Order circuits by qubit count (then index) and map to plot positions\n",
        "    ref = sorted(\n",
        "        [r for r in results if r[\"method\"] == methods[0]],\n",
        "        key=lambda r: (r[\"num_qubits\"], r[\"qc_index\"]),\n",
        "    )\n",
        "    pos_map = {r[\"qc_index\"]: pos for pos, r in enumerate(ref)}\n",
        "    tick_positions = [pos_map[r[\"qc_index\"]] for r in ref]\n",
        "    tick_labels = [\n",
        "        f\"{pos_map[r['qc_index']]} ({r['num_qubits']}q)\" for r in ref\n",
        "    ]\n",
        "\n",
        "    metrics = [\n",
        "        (\"two_qubit_depth\", \"Two-Qubit Depth\"),\n",
        "        (\"size\", \"Total Gate Count (Circuit Size)\"),\n",
        "        (\"runtime\", \"Transpilation Runtime (s)\"),\n",
        "    ]\n",
        "\n",
        "    fig, axes = plt.subplots(1, 3, figsize=(20, 5.5))\n",
        "    fig.suptitle(title_prefix, fontsize=15, fontweight=\"bold\", y=1.02)\n",
        "\n",
        "    for ax, (metric, ylabel) in zip(axes, metrics):\n",
        "        for method in methods:\n",
        "            subset = sorted(\n",
        "                [r for r in results if r[\"method\"] == method],\n",
        "                key=lambda r: pos_map[r[\"qc_index\"]],\n",
        "            )\n",
        "            ax.plot(\n",
        "                [pos_map[r[\"qc_index\"]] for r in subset],\n",
        "                [r[metric] for r in subset],\n",
        "                marker=markers.get(method, \"o\"),\n",
        "                label=method,\n",
        "                color=palette.get(method, None),\n",
        "                linewidth=1.5,\n",
        "                markersize=6,\n",
        "                alpha=0.85,\n",
        "            )\n",
        "        ax.set_xlabel(\"Circuit Index (num qubits)\", fontsize=11)\n",
        "        ax.set_ylabel(ylabel, fontsize=11)\n",
        "        ax.legend(frameon=True, fontsize=9)\n",
        "        ax.grid(True, linestyle=\"--\", alpha=0.4)\n",
        "        step = max(1, len(tick_positions) // 15)\n",
        "        ax.set_xticks(tick_positions[::step])\n",
        "        ax.set_xticklabels(\n",
        "            [tick_labels[i] for i in range(0, len(tick_labels), step)],\n",
        "            fontsize=7,\n",
        "            rotation=45,\n",
        "            ha=\"right\",\n",
        "        )\n",
        "\n",
        "    plt.tight_layout()\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "pct_improvement_plot_fn",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_pct_improvement_vs_sabre(results, title_prefix):\n",
        "    \"\"\"\n",
        "    Plot the per-circuit percent improvement of each non-SABRE method\n",
        "    relative to SABRE, for each metric. A positive value means the\n",
        "    method improved on SABRE; negative means SABRE was better.\n",
        "    \"\"\"\n",
        "    metrics = [\n",
        "        (\"two_qubit_depth\", \"2Q Depth\"),\n",
        "        (\"size\", \"Gate Count\"),\n",
        "        (\"runtime\", \"Runtime\"),\n",
        "    ]\n",
        "    palette = {\"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n",
        "    markers = {\"AI\": \"^\", \"Rustiq\": \"s\"}\n",
        "\n",
        "    methods = _method_order(results)\n",
        "    sabre = sorted(\n",
        "        [r for r in results if r[\"method\"] == \"SABRE\"],\n",
        "        key=lambda r: (r[\"num_qubits\"], r[\"qc_index\"]),\n",
        "    )\n",
        "    other_methods = [m for m in methods if m != \"SABRE\"]\n",
        "\n",
        "    tick_positions = list(range(len(sabre)))\n",
        "    tick_labels = [\n",
        "        f\"{i} ({sabre[i]['num_qubits']}q)\" for i in range(len(sabre))\n",
        "    ]\n",
        "\n",
        "    fig, axes = plt.subplots(1, 3, figsize=(20, 5.5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: % Improvement over SABRE\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "\n",
        "    for ax, (metric, label) in zip(axes, metrics):\n",
        "        ax.axhline(0, color=\"#1f77b4\", linewidth=2, label=\"SABRE (baseline)\")\n",
        "        for method in other_methods:\n",
        "            data = sorted(\n",
        "                [r for r in results if r[\"method\"] == method],\n",
        "                key=lambda r: (r[\"num_qubits\"], r[\"qc_index\"]),\n",
        "            )\n",
        "            pct = [\n",
        "                (sabre[i][metric] - data[i][metric]) / sabre[i][metric] * 100\n",
        "                for i in range(len(sabre))\n",
        "            ]\n",
        "            ax.plot(\n",
        "                tick_positions,\n",
        "                pct,\n",
        "                marker=markers.get(method, \"o\"),\n",
        "                label=method,\n",
        "                color=palette.get(method, None),\n",
        "                linewidth=1.5,\n",
        "                markersize=6,\n",
        "                alpha=0.85,\n",
        "            )\n",
        "        ax.set_xlabel(\"Circuit Index (num qubits)\", fontsize=11)\n",
        "        ax.set_ylabel(f\"% Improvement ({label})\", fontsize=11)\n",
        "        ax.legend(frameon=True, fontsize=9)\n",
        "        ax.grid(True, linestyle=\"--\", alpha=0.4)\n",
        "        step = max(1, len(tick_positions) // 15)\n",
        "        ax.set_xticks(tick_positions[::step])\n",
        "        ax.set_xticklabels(\n",
        "            [tick_labels[i] for i in range(0, len(tick_labels), step)],\n",
        "            fontsize=7,\n",
        "            rotation=45,\n",
        "            ha=\"right\",\n",
        "        )\n",
        "        ylims = ax.get_ylim()\n",
        "        ax.axhspan(0, max(ylims[1], 1), alpha=0.04, color=\"green\")\n",
        "        ax.axhspan(min(ylims[0], -1), 0, alpha=0.04, color=\"red\")\n",
        "\n",
        "    plt.tight_layout()\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "d5e6f7a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d5e6f7a8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_transpilation_comparison(\n",
        "    results_small,\n",
        "    \"Small-Scale Hamiltonian Circuits: Compilation Comparison\",\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "pct_improvement_small",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_small-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_pct_improvement_vs_sabre(\n",
        "    results_small,\n",
        "    \"Small-Scale Hamiltonian Circuits\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d2a29d04",
      "metadata": {},
      "source": [
        "A questo livello, tutti e tre i responsabili della gestione dei passaggi ottengono buoni risultati, e le loro medie sono molto simili tra loro. Ciò è dovuto in gran parte al fatto che i circuiti di piccole dimensioni lasciano poco spazio a ulteriori ottimizzazioni, per cui i metodi tendono a convergere su soluzioni simili.\n",
        "\n",
        "In questo esempio, Rustiq produce i risultati più variabili, con i valori anomali più elevati sia per quanto riguarda la profondità a due qubit che il numero di gate. Sebbene questa variabilità comporti che a volte Rustiq rimanga indietro, significa anche che, occasionalmente, Rustiq trova soluzioni migliori rispetto agli altri due metodi. Il transpiler basato sull'intelligenza artificiale offre risultati più stabili rispetto a SABRE e Rustiq, con prestazioni molto simili su gran parte dei circuiti e senza molti valori anomali.\n",
        "\n",
        "In termini di prestazioni, sia SABRE che Rustiq sono veloci, mentre il transpiler basato sull'intelligenza artificiale risulta notevolmente più lento su alcuni circuiti.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e5f6a7b8",
      "metadata": {},
      "source": [
        "<span id=\"best-performing-method-by-metric\" />\n",
        "\n",
        "#### Metodo con le migliori prestazioni in base all'indicatore\n",
        "\n",
        "Il grafico sottostante mostra con quale frequenza ciascun metodo ha raggiunto il valore migliore (più basso) per ciascuna metrica. Sono possibili situazioni di parità: per i circuiti più semplici, diversi metodi possono raggiungere la stessa profondità ottimale a due qubit o lo stesso numero di gate. In caso di parità, vengono conteggiati tutti i metodi a pari merito, pertanto la somma delle percentuali relative a una determinata metrica potrebbe superare il 100%.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "f5a6b7c8",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_best_method_bars(results, metrics_list=None):\n",
        "    \"\"\"\n",
        "    Plot a grouped bar chart showing the percentage of circuits\n",
        "    where each method achieved the best (lowest) value for each metric.\n",
        "\n",
        "    Ties are counted for all tied methods, so percentages per metric\n",
        "    can sum to more than 100%.\n",
        "    \"\"\"\n",
        "    if metrics_list is None:\n",
        "        metrics_list = [\"two_qubit_depth\", \"size\", \"runtime\"]\n",
        "\n",
        "    labels = {\n",
        "        \"two_qubit_depth\": \"2Q Depth\",\n",
        "        \"size\": \"Gate Count\",\n",
        "        \"runtime\": \"Runtime\",\n",
        "    }\n",
        "    methods = _method_order(results)\n",
        "    palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n",
        "\n",
        "    by_index = {}\n",
        "    for r in results:\n",
        "        by_index.setdefault(r[\"qc_index\"], []).append(r)\n",
        "    n_circuits = len(by_index)\n",
        "\n",
        "    win_data = {m: [] for m in methods}\n",
        "    tie_counts = []\n",
        "    metric_labels = []\n",
        "\n",
        "    for metric in metrics_list:\n",
        "        metric_labels.append(\n",
        "            labels.get(metric, metric.replace(\"_\", \" \").title())\n",
        "        )\n",
        "        counts = Counter()\n",
        "        ties = 0\n",
        "        for group in by_index.values():\n",
        "            min_val = min(r[metric] for r in group)\n",
        "            best = [r[\"method\"] for r in group if r[metric] == min_val]\n",
        "            if len(best) > 1:\n",
        "                ties += 1\n",
        "            counts.update(best)\n",
        "        tie_counts.append(ties)\n",
        "        for m in methods:\n",
        "            win_data[m].append(counts.get(m, 0) / n_circuits * 100)\n",
        "\n",
        "    x = np.arange(len(metric_labels))\n",
        "    width = 0.22\n",
        "    fig, ax = plt.subplots(figsize=(8, 5))\n",
        "\n",
        "    for i, method in enumerate(methods):\n",
        "        bars = ax.bar(\n",
        "            x + i * width,\n",
        "            win_data[method],\n",
        "            width,\n",
        "            label=method,\n",
        "            color=palette.get(method, None),\n",
        "            edgecolor=\"black\",\n",
        "            linewidth=0.5,\n",
        "        )\n",
        "        for bar in bars:\n",
        "            height = bar.get_height()\n",
        "            if height > 0:\n",
        "                ax.text(\n",
        "                    bar.get_x() + bar.get_width() / 2,\n",
        "                    height + 1.5,\n",
        "                    f\"{height:.0f}%\",\n",
        "                    ha=\"center\",\n",
        "                    va=\"bottom\",\n",
        "                    fontsize=9,\n",
        "                )\n",
        "\n",
        "    # Annotate tie counts below each metric label\n",
        "    for j, ties in enumerate(tie_counts):\n",
        "        if ties > 0:\n",
        "            ax.text(\n",
        "                x[j] + width,\n",
        "                -8,\n",
        "                f\"({ties} tie{'s' if ties != 1 else ''})\",\n",
        "                ha=\"center\",\n",
        "                va=\"top\",\n",
        "                fontsize=8,\n",
        "                color=\"gray\",\n",
        "            )\n",
        "\n",
        "    ax.set_xticks(x + width)\n",
        "    ax.set_xticklabels(metric_labels, fontsize=11)\n",
        "    ax.set_ylabel(\"Circuits with best value (%)\", fontsize=11)\n",
        "    ax.set_title(\n",
        "        \"Best-Performing Method by Metric (ties counted for all tied methods)\",\n",
        "        fontsize=12,\n",
        "        fontweight=\"bold\",\n",
        "    )\n",
        "    ax.legend(frameon=True, fontsize=10)\n",
        "    ax.set_ylim(-12, 120)\n",
        "    ax.yaxis.set_major_formatter(ticker.PercentFormatter())\n",
        "    ax.grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n",
        "\n",
        "    plt.tight_layout()\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "a6b7c8d9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/a6b7c8d9-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_best_method_bars(results_small)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30687d7b",
      "metadata": {},
      "source": [
        "In questo esempio, i tre metodi danno risultati molto simili sui circuiti di piccole dimensioni. Per quanto riguarda la profondità a due qubit e il numero di gate, la percentuale di circuiti in cui ciascun metodo risulta il migliore è simile (circa il 35–55%), e molti circuiti finiscono in parità poiché i circuiti più semplici hanno spesso un’unica soluzione ottimale che viene individuata da più metodi. La differenza più evidente riguarda il tempo di esecuzione: SABRE e Rustiq risultano i più veloci ciascuno su circa la metà dei circuiti, mentre il transpiler basato sull’intelligenza artificiale raramente è il più veloce. Considerando tutti e tre gli indicatori nel loro insieme, Rustiq presenta un leggero vantaggio complessivo: è il vincitore più frequente in termini di profondità a due qubit e rimane competitivo per quanto riguarda il numero di gate e il tempo di esecuzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6c7d8e9",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n",
        "Per valutare in che modo la qualità della transpilazione influisca sull'esecuzione in presenza di rumore, utilizziamo una tecnica **basata su circuiti speculari**. Per ogni circuito transpilato $U$, aggiungiamo il suo inverso $U^\\dagger$ in modo che il circuito combinato $U^\\dagger U$ sia, in teoria, l'identità. Partendo dallo stato “ $|0\\rangle$ ”, un’esecuzione perfetta (priva di rumore) restituirebbe la stringa di bit composta interamente da zeri con probabilità 1.\n",
        "\n",
        "In pratica, gli errori a livello di porta si accumulano lungo tutto il circuito, per cui la probabilità di recuperare un $|0\\rangle^{\\otimes n}$ e diminuisce. Un metodo di compilazione che genera un circuito meno profondo con un numero inferiore di porte accumulerà meno rumore.\n",
        "\n",
        "L'approccio basato sui circuiti a specchio è di una semplicità accattivante e si adatta a circuiti di qualsiasi dimensione, poiché l'uscita prevista è sempr $|0\\rangle^{\\otimes n}$ e e non è necessaria alcuna simulazione classica dello stato ideale. Tuttavia, occorre tenere presenti le seguenti avvertenze: il circuito speculare è un surrogato del circuito effettivo (non il circuito stesso), raddoppia il numero di porte logiche (il che esagera l’effetto del rumore) e può sottostimare determinati errori quando il rumore si annulla simmetricamente lungo il confine dello specchio.\n",
        "\n",
        "Scegliamo l'indice di circuito 6 dall'insieme su piccola scala ed eseguiamo i circuiti speculari su un simulatore Aer con un semplice modello di rumore depolarizzante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "c6d7e8f9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Test circuit: all-vib-fccf, 4 qubits\n",
            "\n",
            "Transpilation metrics for circuit index 6:\n",
            "  SABRE     2Q depth=   66  size=   339\n",
            "  AI        2Q depth=   65  size=   300\n",
            "  Rustiq    2Q depth=   34  size=   193\n"
          ]
        }
      ],
      "source": [
        "# Select circuit index 6 from the small-scale transpiled circuits\n",
        "test_idx = 6\n",
        "test_circuit = qc_small[test_idx]\n",
        "print(f\"Test circuit: {test_circuit.name}, {test_circuit.num_qubits} qubits\")\n",
        "\n",
        "# Get the transpiled versions\n",
        "tqc_methods_small = {\n",
        "    \"SABRE\": tqc_sabre_small[test_idx],\n",
        "    \"AI\": tqc_ai_small[test_idx],\n",
        "    \"Rustiq\": tqc_rustiq_small[test_idx],\n",
        "}\n",
        "\n",
        "# Show transpilation metrics for this circuit\n",
        "print(f\"\\nTranspilation metrics for circuit index {test_idx}:\")\n",
        "for method, tqc in tqc_methods_small.items():\n",
        "    depth_2q = tqc.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    size = tqc.size()\n",
        "    print(f\"  {method:8s}  2Q depth={depth_2q:5d}  size={size:6d}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d6e7f8a9",
      "metadata": {},
      "source": [
        "Realizzare i circuiti speculari (vedere $U^\\dagger$ ), rimappare gli indici dei qubit in modo che siano contigui, in modo che il simulatore gestisca solo i qubit attivi, ed eseguire il codice su un simulatore Aer soggetto a rumore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "e6f7a8b9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SABRE     P(|00...0>) = 0.7796  (7796/10000)\n",
            "AI        P(|00...0>) = 0.8073  (8073/10000)\n",
            "Rustiq    P(|00...0>) = 0.8923  (8923/10000)\n"
          ]
        }
      ],
      "source": [
        "def remap_to_contiguous(tqc):\n",
        "    \"\"\"Remap a transpiled circuit to use contiguous qubit indices.\n",
        "\n",
        "    Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)\n",
        "    on a large backend. This remaps them to 0, 1, 2, ... so Aer only\n",
        "    simulates the active qubits.\n",
        "    \"\"\"\n",
        "    active = sorted(\n",
        "        {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}\n",
        "    )\n",
        "    qubit_map = {old: new for new, old in enumerate(active)}\n",
        "    new_qc = QuantumCircuit(len(active))\n",
        "    for inst in tqc.data:\n",
        "        old_indices = [tqc.find_bit(q).index for q in inst.qubits]\n",
        "        new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])\n",
        "    return new_qc\n",
        "\n",
        "\n",
        "def build_mirror_circuit(tqc):\n",
        "    \"\"\"Build a mirror circuit: U followed by U-dagger, with measurements.\n",
        "\n",
        "    The combined circuit U-dagger @ U should be the identity, so measuring\n",
        "    all zeros indicates a noise-free execution.\n",
        "    \"\"\"\n",
        "    tqc_compact = remap_to_contiguous(tqc)\n",
        "    mirror = tqc_compact.compose(tqc_compact.inverse())\n",
        "    mirror.measure_all()\n",
        "    return mirror\n",
        "\n",
        "\n",
        "# Build a simple depolarizing noise model\n",
        "noise_model = NoiseModel()\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.001, 1),\n",
        "    [\"sx\", \"x\", \"rz\"],  # ~0.1% per 1Q gate\n",
        ")\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.01, 2),\n",
        "    [\"cx\", \"ecr\"],  # ~1% per 2Q gate\n",
        ")\n",
        "\n",
        "aer_sim = AerSimulator(noise_model=noise_model)\n",
        "\n",
        "shots = 10000\n",
        "fidelities = {}\n",
        "\n",
        "for method, tqc in tqc_methods_small.items():\n",
        "    mirror = build_mirror_circuit(tqc)\n",
        "\n",
        "    sampler = SamplerV2(mode=aer_sim)\n",
        "    job = sampler.run([mirror], shots=shots)\n",
        "    result = job.result()\n",
        "    counts = result[0].data.meas.get_counts()\n",
        "\n",
        "    # Fidelity = fraction of all-zeros (error-free) outcomes\n",
        "    n_qubits = mirror.num_qubits - mirror.num_clbits  # active qubits\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    fidelity = counts.get(all_zeros, 0) / shots\n",
        "    fidelities[method] = fidelity\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {fidelity:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "small_step4_plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/small_step4_plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "def plot_mirror_results(tqc_methods, fidelities, circuit_name):\n",
        "    \"\"\"\n",
        "    Plot a three-panel comparison: fidelity, 2Q depth,\n",
        "    and gate count for each compilation method.\n",
        "    \"\"\"\n",
        "    methods = list(tqc_methods.keys())\n",
        "    palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n",
        "    colors = [palette.get(m, \"gray\") for m in methods]\n",
        "\n",
        "    fidelity_vals = [fidelities[m] for m in methods]\n",
        "    depth_vals = [\n",
        "        tqc_methods[m].depth(lambda x: x.operation.num_qubits == 2)\n",
        "        for m in methods\n",
        "    ]\n",
        "    size_vals = [tqc_methods[m].size() for m in methods]\n",
        "\n",
        "    fig, axes = plt.subplots(1, 3, figsize=(16, 5))\n",
        "    fig.suptitle(\n",
        "        f\"Mirror Circuit Results: {circuit_name}\",\n",
        "        fontsize=14,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "\n",
        "    def _annotate_bars(ax, bars, values, fmt=\"{}\"):\n",
        "        ymax = ax.get_ylim()[1]\n",
        "        for bar, val in zip(bars, values):\n",
        "            label = fmt.format(val)\n",
        "            y = val + ymax * 0.03\n",
        "            ax.text(\n",
        "                bar.get_x() + bar.get_width() / 2,\n",
        "                y,\n",
        "                label,\n",
        "                ha=\"center\",\n",
        "                va=\"bottom\",\n",
        "                fontsize=10,\n",
        "                fontweight=\"bold\",\n",
        "            )\n",
        "\n",
        "    # Panel 1: Survival Probability\n",
        "    bars = axes[0].bar(\n",
        "        methods, fidelity_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n",
        "    )\n",
        "    axes[0].set_ylabel(\"Fidelity  P(|00...0>)\", fontsize=11)\n",
        "    axes[0].set_title(\"Fidelity (higher is better)\", fontsize=12)\n",
        "    axes[0].set_ylim(\n",
        "        0, max(fidelity_vals) * 1.18 if max(fidelity_vals) > 0 else 1.0\n",
        "    )\n",
        "    axes[0].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n",
        "    _annotate_bars(axes[0], bars, fidelity_vals, fmt=\"{:.4f}\")\n",
        "\n",
        "    # Panel 2: Two-Qubit Depth\n",
        "    bars = axes[1].bar(\n",
        "        methods, depth_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n",
        "    )\n",
        "    axes[1].set_ylabel(\"Two-Qubit Depth\", fontsize=11)\n",
        "    axes[1].set_title(\"2Q Depth (lower is better)\", fontsize=12)\n",
        "    axes[1].set_ylim(0, max(depth_vals) * 1.18)\n",
        "    axes[1].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n",
        "    _annotate_bars(axes[1], bars, depth_vals)\n",
        "\n",
        "    # Panel 3: Gate Count\n",
        "    bars = axes[2].bar(\n",
        "        methods, size_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n",
        "    )\n",
        "    axes[2].set_ylabel(\"Total Gate Count\", fontsize=11)\n",
        "    axes[2].set_title(\"Gate Count (lower is better)\", fontsize=12)\n",
        "    axes[2].set_ylim(0, max(size_vals) * 1.18)\n",
        "    axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n",
        "    _annotate_bars(axes[2], bars, size_vals)\n",
        "\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "plot_mirror_results(tqc_methods_small, fidelities, test_circuit.name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "small_mirror_commentary",
      "metadata": {},
      "source": [
        "<span id=\"observations\" />\n",
        "\n",
        "#### Osservazioni\n",
        "\n",
        "Il metodo con la profondità di due qubit più bassa e il minor numero di porte raggiunge la fedeltà più elevata, in linea con l’aspettativa secondo cui i circuiti più corti accumulano meno rumore. Anche differenze minime in termini di profondità e numero di porte si traducono in differenze misurabili nella fedeltà nell'ambito del modello di rumore depolarizzante.\n",
        "\n",
        "Si tenga presente che questi risultati si riferiscono a un singolo circuito. La classifica relativa dei metodi può variare da circuito a circuito a seconda della struttura dell'hamiltoniano.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sec-large-scale",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n",
        "In questa sezione, mettiamo a confronto gli stessi tre metodi di compilazione su circuiti hamiltoniani con 20 o più qubit. Questi circuiti sono più rappresentativi dei carichi di lavoro pratici della simulazione hamiltoniana e consentono di verificare come ciascun metodo si adatti in termini di qualità del circuito e tempo di compilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f6a7b8c9",
      "metadata": {},
      "source": [
        "<span id=\"steps-1-4-combined\" />\n",
        "\n",
        "### Passaggi da 1 a 4 combinati\n",
        "\n",
        "Il flusso di lavoro segue la stessa struttura dell'esempio su piccola scala. Effettuiamo la transpilazione di tutti i circuiti su larga scala con ciascun metodo, raccogliamo le metriche e inviamo un circuito speculare a un hardware quantistico reale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "a7b8c9d0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[SABRE] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=258, time=0.16s\n",
            "[SABRE] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=345, size=4036, time=0.08s\n",
            "[SABRE] Circuit 2 (TSP_Ncity-5): 2Q depth=187, size=2045, time=0.04s\n",
            "[SABRE] Circuit 3 (tfim): 2Q depth=100, size=489, time=0.21s\n",
            "[SABRE] Circuit 4 (all-vib-h2co): 2Q depth=30, size=570, time=0.18s\n",
            "[SABRE] Circuit 5 (uuf100-ham): 2Q depth=414, size=4779, time=0.09s\n",
            "[SABRE] Circuit 6 (uuf100-ham): 2Q depth=523, size=5667, time=0.11s\n",
            "[SABRE] Circuit 7 (graph-gnp_k-4): 2Q depth=3028, size=24885, time=0.39s\n",
            "[SABRE] Circuit 8 (uf100-ham): 2Q depth=700, size=8271, time=0.15s\n",
            "[SABRE] Circuit 9 (uf100-ham): 2Q depth=698, size=8957, time=0.15s\n",
            "[SABRE] Circuit 10 (TSP_Ncity-7): 2Q depth=432, size=6353, time=0.12s\n",
            "[SABRE] Circuit 11 (all-vib-cyclo_propene): 2Q depth=30, size=1144, time=0.20s\n",
            "[SABRE] Circuit 12 (TSP_Ncity-8): 2Q depth=704, size=10287, time=0.18s\n",
            "[SABRE] Circuit 13 (uf100-ham): 2Q depth=2454, size=30195, time=0.46s\n",
            "[SABRE] Circuit 14 (tfim): 2Q depth=245, size=3670, time=0.08s\n",
            "[SABRE] Circuit 15 (flat100-ham): 2Q depth=154, size=3836, time=0.12s\n",
            "[SABRE] Circuit 16 (graph-regular_reg-4): 2Q depth=863, size=14063, time=0.22s\n",
            "[SABRE] Circuit 17 (tfim): 2Q depth=581, size=8810, time=0.15s\n",
            "[SABRE] Circuit 18 (FH_D-1): 2Q depth=1704, size=9528, time=0.35s\n",
            "[SABRE] Circuit 19 (TSP_Ncity-10): 2Q depth=1091, size=22041, time=0.38s\n",
            "[SABRE] Circuit 20 (TSP_Ncity-10): 2Q depth=1091, size=22005, time=0.38s\n",
            "[SABRE] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=224, size=8321, time=0.17s\n",
            "[AI] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=258, time=0.17s\n",
            "[AI] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=323, size=4418, time=3.13s\n",
            "[AI] Circuit 2 (TSP_Ncity-5): 2Q depth=161, size=2229, time=1.47s\n",
            "[AI] Circuit 3 (tfim): 2Q depth=20, size=402, time=0.34s\n",
            "[AI] Circuit 4 (all-vib-h2co): 2Q depth=38, size=661, time=0.19s\n",
            "[AI] Circuit 5 (uuf100-ham): 2Q depth=391, size=5130, time=3.27s\n",
            "[AI] Circuit 6 (uuf100-ham): 2Q depth=463, size=6095, time=4.23s\n",
            "[AI] Circuit 7 (graph-gnp_k-4): 2Q depth=3207, size=25641, time=15.15s\n",
            "[AI] Circuit 8 (uf100-ham): 2Q depth=637, size=8267, time=5.87s\n",
            "[AI] Circuit 9 (uf100-ham): 2Q depth=632, size=9330, time=7.29s\n",
            "[AI] Circuit 10 (TSP_Ncity-7): 2Q depth=452, size=7418, time=6.02s\n",
            "[AI] Circuit 11 (all-vib-cyclo_propene): 2Q depth=38, size=1323, time=0.27s\n",
            "[AI] Circuit 12 (TSP_Ncity-8): 2Q depth=609, size=11131, time=10.07s\n",
            "[AI] Circuit 13 (uf100-ham): 2Q depth=2251, size=31128, time=38.77s\n",
            "[AI] Circuit 14 (tfim): 2Q depth=165, size=3460, time=1.64s\n",
            "[AI] Circuit 15 (flat100-ham): 2Q depth=91, size=3497, time=2.49s\n",
            "[AI] Circuit 16 (graph-regular_reg-4): 2Q depth=664, size=15256, time=12.35s\n",
            "[AI] Circuit 17 (tfim): 2Q depth=583, size=9157, time=6.28s\n",
            "[AI] Circuit 18 (FH_D-1): 2Q depth=1193, size=7754, time=4.54s\n",
            "[AI] Circuit 19 (TSP_Ncity-10): 2Q depth=1134, size=22577, time=25.64s\n",
            "[AI] Circuit 20 (TSP_Ncity-10): 2Q depth=1172, size=23851, time=28.97s\n",
            "[AI] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=219, size=8600, time=8.85s\n",
            "[Rustiq] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=257, time=0.16s\n",
            "[Rustiq] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=640, size=5831, time=0.13s\n",
            "[Rustiq] Circuit 2 (TSP_Ncity-5): 2Q depth=408, size=3985, time=0.08s\n",
            "[Rustiq] Circuit 3 (tfim): 2Q depth=31, size=688, time=0.07s\n",
            "[Rustiq] Circuit 4 (all-vib-h2co): 2Q depth=65, size=1058, time=2.91s\n",
            "[Rustiq] Circuit 5 (uuf100-ham): 2Q depth=633, size=6757, time=0.14s\n",
            "[Rustiq] Circuit 6 (uuf100-ham): 2Q depth=795, size=8495, time=0.17s\n",
            "[Rustiq] Circuit 7 (graph-gnp_k-4): 2Q depth=13768, size=139793, time=2.92s\n",
            "[Rustiq] Circuit 8 (uf100-ham): 2Q depth=1099, size=11878, time=0.25s\n",
            "[Rustiq] Circuit 9 (uf100-ham): 2Q depth=911, size=11111, time=0.22s\n",
            "[Rustiq] Circuit 10 (TSP_Ncity-7): 2Q depth=1183, size=13197, time=0.27s\n",
            "[Rustiq] Circuit 11 (all-vib-cyclo_propene): 2Q depth=67, size=2491, time=13.56s\n",
            "[Rustiq] Circuit 12 (TSP_Ncity-8): 2Q depth=1615, size=21358, time=0.48s\n",
            "[Rustiq] Circuit 13 (uf100-ham): 2Q depth=2920, size=40465, time=0.91s\n",
            "[Rustiq] Circuit 14 (tfim): 2Q depth=489, size=6552, time=0.15s\n",
            "[Rustiq] Circuit 15 (flat100-ham): 2Q depth=378, size=5906, time=0.14s\n",
            "[Rustiq] Circuit 16 (graph-regular_reg-4): 2Q depth=12163, size=168679, time=2.94s\n",
            "[Rustiq] Circuit 17 (tfim): 2Q depth=1208, size=17042, time=0.36s\n",
            "[Rustiq] Circuit 18 (FH_D-1): 2Q depth=1061, size=24000, time=0.47s\n",
            "[Rustiq] Circuit 19 (TSP_Ncity-10): 2Q depth=2565, size=41340, time=1.38s\n",
            "[Rustiq] Circuit 20 (TSP_Ncity-10): 2Q depth=2565, size=41275, time=1.38s\n",
            "[Rustiq] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=808, size=17548, time=0.42s\n"
          ]
        }
      ],
      "source": [
        "results_large = []\n",
        "\n",
        "tqc_sabre_large = capture_transpilation_metrics(\n",
        "    results_large, pm_sabre, qc_large, \"SABRE\"\n",
        ")\n",
        "tqc_ai_large = capture_transpilation_metrics(\n",
        "    results_large, pm_ai, qc_large, \"AI\"\n",
        ")\n",
        "tqc_rustiq_large = capture_transpilation_metrics(\n",
        "    results_large, pm_rustiq, qc_large, \"Rustiq\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "b7c8d9e0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mean +/- std per compilation method\n",
            "Method                2Q Depth              Gate Count             Runtime (s)\n",
            "------------------------------------------------------------------------------\n",
            "SABRE          709.1 +/- 783.8     9,100.5 +/- 8,493.1             0.2 +/- 0.1\n",
            "AI             656.6 +/- 777.5     9,435.6 +/- 8,853.0            8.5 +/- 10.2\n",
            "Rustiq     2,062.5 +/- 3,631.1   26,804.8 +/- 43,403.1             1.3 +/- 2.9\n",
            "\n",
            "Mean % improvement vs SABRE (positive = better than SABRE)\n",
            "Method                2Q Depth              Gate Count             Runtime (s)\n",
            "------------------------------------------------------------------------------\n",
            "AI             +9.6% +/- 22.8%          -3.4% +/- 9.4%    -3620.0% +/- 2405.5%\n",
            "Rustiq      -154.5% +/- 273.9%      -137.1% +/- 233.2%     -527.0% +/- 1405.5%\n"
          ]
        }
      ],
      "source": [
        "print_summary_table(results_large)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "per_circuit_large_code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2Q Depth (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-hc3h2cn   24       2*       2*       2*\n",
            "  1  ham-graph-gnp_k-  24      345     323*      640\n",
            "  2  TSP_Ncity-5       25      187     161*      408\n",
            "  3  tfim              26      100      20*       31\n",
            "  4  all-vib-h2co      32      30*       38       65\n",
            "  5  uuf100-ham        40      414     391*      633\n",
            "  6  uuf100-ham        40      523     463*      795\n",
            "  7  graph-gnp_k-4     40    3028*     3207    13768\n",
            "\n",
            "Gate Count (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-hc3h2cn   24      258      258     257*\n",
            "  1  ham-graph-gnp_k-  24    4036*     4418     5831\n",
            "  2  TSP_Ncity-5       25    2045*     2229     3985\n",
            "  3  tfim              26      489     402*      688\n",
            "  4  all-vib-h2co      32     570*      661     1058\n",
            "  5  uuf100-ham        40    4779*     5130     6757\n",
            "  6  uuf100-ham        40    5667*     6095     8495\n",
            "  7  graph-gnp_k-4     40   24885*    25641   139793\n",
            "\n",
            "Runtime (s) (first 8 circuits by qubit count); * = best\n",
            "Idx  Circuit            Q    SABRE       AI   Rustiq\n",
            "----------------------------------------------------\n",
            "  0  all-vib-hc3h2cn   24     0.16     0.17    0.16*\n",
            "  1  ham-graph-gnp_k-  24    0.08*     3.13     0.13\n",
            "  2  TSP_Ncity-5       25    0.04*     1.47     0.08\n",
            "  3  tfim              26     0.21     0.34    0.07*\n",
            "  4  all-vib-h2co      32    0.18*     0.19     2.91\n",
            "  5  uuf100-ham        40    0.09*     3.27     0.14\n",
            "  6  uuf100-ham        40    0.11*     4.23     0.17\n",
            "  7  graph-gnp_k-4     40    0.39*    15.15     2.92\n",
            "\n"
          ]
        }
      ],
      "source": [
        "print_per_circuit_comparison(results_large, num_rows=8)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "c7d8e9f0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/c7d8e9f0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_transpilation_comparison(\n",
        "    results_large,\n",
        "    \"Large-Scale Hamiltonian Circuits: Compilation Comparison\",\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "pct_improvement_large",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_large-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_pct_improvement_vs_sabre(\n",
        "    results_large,\n",
        "    \"Large-Scale Hamiltonian Circuits\",\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "d7e8f9a0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d7e8f9a0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_best_method_bars(results_large)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "f7a8b9c0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Test circuit: tfim, 26 qubits\n",
            "\n",
            "Transpilation metrics for circuit index 3:\n",
            "  SABRE     2Q depth=  100  size=   489\n",
            "  AI        2Q depth=   20  size=   402\n",
            "  Rustiq    2Q depth=   31  size=   688\n"
          ]
        }
      ],
      "source": [
        "# Select circuit index 3 from the large-scale transpiled circuits\n",
        "test_idx_large = 3\n",
        "test_circuit_large = qc_large[test_idx_large]\n",
        "print(\n",
        "    f\"Test circuit: {test_circuit_large.name}, {test_circuit_large.num_qubits} qubits\"\n",
        ")\n",
        "\n",
        "tqc_methods_large = {\n",
        "    \"SABRE\": tqc_sabre_large[test_idx_large],\n",
        "    \"AI\": tqc_ai_large[test_idx_large],\n",
        "    \"Rustiq\": tqc_rustiq_large[test_idx_large],\n",
        "}\n",
        "\n",
        "print(f\"\\nTranspilation metrics for circuit index {test_idx_large}:\")\n",
        "for method, tqc in tqc_methods_large.items():\n",
        "    depth_2q = tqc.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    size = tqc.size()\n",
        "    print(f\"  {method:8s}  2Q depth={depth_2q:5d}  size={size:6d}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "3cd3afa2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "SABRE transpiled circuit:\n",
            "OrderedDict({'sx': 211, 'rz': 163, 'cz': 104, 'x': 11})\n",
            "SABRE mirror circuit count ops:\n",
            "OrderedDict({'rz': 1170, 'sx': 422, 'cz': 208, 'measure': 156, 'x': 22, 'barrier': 1})\n",
            "\n",
            "AI transpiled circuit:\n",
            "OrderedDict({'sx': 165, 'rz': 162, 'cz': 68, 'x': 7})\n",
            "AI mirror circuit count ops:\n",
            "OrderedDict({'rz': 984, 'sx': 330, 'measure': 156, 'cz': 136, 'x': 14, 'barrier': 1})\n",
            "\n",
            "Rustiq transpiled circuit:\n",
            "OrderedDict({'sx': 316, 'rz': 225, 'cz': 140, 'x': 7})\n",
            "Rustiq mirror circuit count ops:\n",
            "OrderedDict({'rz': 1714, 'sx': 632, 'cz': 280, 'measure': 156, 'x': 14, 'barrier': 1})\n"
          ]
        }
      ],
      "source": [
        "pm_mirror = generate_preset_pass_manager(\n",
        "    optimization_level=0, backend=backend\n",
        ")\n",
        "\n",
        "for method, tqc in tqc_methods_large.items():\n",
        "    # print the count ops for each circuit\n",
        "    mirror = tqc.copy()\n",
        "    mirror.compose(tqc.inverse(), inplace=True)\n",
        "    mirror.measure_all()\n",
        "    mirror = pm_mirror.run(mirror)\n",
        "    print(f\"\\n{method} transpiled circuit:\")\n",
        "    print(tqc.count_ops())\n",
        "    print(f\"{method} mirror circuit count ops:\")\n",
        "    print(mirror.count_ops())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "large_hw_submit",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SABRE: submitted job d8gvgq66983c73dqe5og\n",
            "AI: submitted job d8gvgqe6983c73dqe5pg\n",
            "Rustiq: submitted job d8gvgqm6983c73dqe5q0\n"
          ]
        }
      ],
      "source": [
        "# Build mirror circuits and submit to real hardware\n",
        "# The inverse may introduce gates (e.g., sxdg) not in the backend's\n",
        "# basis gate set, so we re-transpile the mirror circuit.\n",
        "pm_mirror = generate_preset_pass_manager(\n",
        "    optimization_level=0, backend=backend\n",
        ")\n",
        "\n",
        "shots_hw = 10000\n",
        "hw_jobs = {}\n",
        "\n",
        "for method, tqc in tqc_methods_large.items():\n",
        "    mirror = tqc.copy()\n",
        "    mirror.compose(tqc.inverse(), inplace=True)\n",
        "    mirror.measure_all()\n",
        "\n",
        "    # Re-transpile at opt level 0 to decompose into basis gates\n",
        "    # without changing the layout or routing\n",
        "    mirror = pm_mirror.run(mirror)\n",
        "\n",
        "    sampler = SamplerV2(mode=backend)\n",
        "    sampler.options.environment.job_tags = [\"TUT_CMHSC\"]\n",
        "    job = sampler.run([mirror], shots=shots_hw)\n",
        "    hw_jobs[method] = job\n",
        "    print(f\"{method}: submitted job {job.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "large_hw_results",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SABRE     P(|00...0>) = 0.0005  (5/10000)\n",
            "AI        P(|00...0>) = 0.3267  (3267/10000)\n",
            "Rustiq    P(|00...0>) = 0.1845  (1845/10000)\n"
          ]
        }
      ],
      "source": [
        "# Retrieve results and compute fidelities\n",
        "fidelities_large = {}\n",
        "\n",
        "for method, job in hw_jobs.items():\n",
        "    result = job.result()\n",
        "    counts = result[0].data.meas.get_counts()\n",
        "\n",
        "    n_qubits = backend.num_qubits\n",
        "    all_zeros = \"0\" * n_qubits\n",
        "    fidelity = counts.get(all_zeros, 0) / shots_hw\n",
        "    fidelities_large[method] = fidelity\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {fidelity:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots_hw})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "large_hw_plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/large_hw_plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_mirror_results(\n",
        "    tqc_methods_large, fidelities_large, test_circuit_large.name\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sec-analysis",
      "metadata": {},
      "source": [
        "<span id=\"analysis-of-compilation-results\" />\n",
        "\n",
        "## Analisi dei risultati della compilazione\n",
        "\n",
        "I benchmark riportati sopra mettono a confronto SABRE, il transpiler basato sull'intelligenza artificiale, e Rustiq su circuiti di simulazione hamiltoniani della raccolta Hamlib, sia su piccola che su larga scala.\n",
        "\n",
        "<span id=\"two-qubit-depth-and-gate-count\" />\n",
        "\n",
        "### Profondità a due qubit e numero di gate\n",
        "\n",
        "Su larga scala, SABRE e il transpiler basato sull’intelligenza artificiale sono i due che ottengono i risultati migliori, e ciascuno di essi è in testa in base a un parametro diverso. Come mostra il grafico *delle metriche*, che indica il metodo più performante, SABRE produce il numero più basso di gate sulla grande maggioranza dei circuiti ed è il metodo più veloce su quasi tutti essi, in linea con un’euristica progettata per ridurre al minimo i gate SWAP inseriti e con le recenti ottimizzazioni apportate al layout e al routing. Il transpiler basato sull'intelligenza artificiale produce la profondità di due qubit più bassa sulla maggior parte dei circuiti, in linea con quella parte del suo obiettivo di apprendimento per rinforzo che mira alla profondità del circuito. La tabella riassuntiva riflette la stessa ripartizione: SABRE presenta il numero medio di gate più basso, mentre il transpiler basato sull’intelligenza artificiale presenta la profondità media a due qubit più bassa. Entrambi i metodi sono coerenti e affidabili su tutta la gamma di circuiti.\n",
        "\n",
        "Rustiq, progettato appositamente per `PauliEvolutionGate` la sintesi, produce il miglior risultato in assoluto solo su una piccola parte dei circuiti su larga scala. I suoi valori medi sono fortemente influenzati da una manciata di valori anomali significativi, visibili come picchi marcati nel grafico di confronto delle compilazioni, in cui Rustiq produce una profondità e un numero di gate notevolmente superiori rispetto agli altri metodi. Senza questi valori anomali, la sua prestazione media sarebbe molto più vicina a quella di SABRE e del transpiler basato sull'intelligenza artificiale.\n",
        "\n",
        "L'osservazione fondamentale è che nessun metodo in particolare risulta predominante su tutti i circuiti. Ciascun metodo offre prestazioni migliori rispetto agli altri in casi specifici, il che rende opportuno provare tutti gli strumenti disponibili e selezionare il risultato migliore per ciascun circuito.\n",
        "\n",
        "<span id=\"runtime\" />\n",
        "\n",
        "### Fase di esecuzione\n",
        "\n",
        "SABRE è sempre il metodo più veloce. Rustiq funziona generalmente a una velocità simile, ma può verificarsi che in alcuni casi la compilazione richieda molto più tempo. Ciò è particolarmente evidente nei risultati su larga scala, dove alcuni circuiti provocano un picco nel tempo di esecuzione di Rustiq. Questi valori anomali incidono notevolmente sul tempo di esecuzione medio, pertanto la mediana potrebbe rappresentare un indicatore più rappresentativo per Rustiq. Il transpiler basato sull'intelligenza artificiale è il più lento dei tre, con un tempo di esecuzione che aumenta notevolmente con circuiti più grandi e complessi.\n",
        "\n",
        "<span id=\"mirror-circuit-results\" />\n",
        "\n",
        "### Risultati del circuito a specchio\n",
        "\n",
        "Gli esperimenti sul circuito a specchio confermano la tendenza prevista: i metodi che producono una profondità di due qubit inferiore e un numero minore di gate raggiungono una fedeltà maggiore in presenza di rumore. Ciò vale sia per il simulatore soggetto a rumore (su piccola scala) che per l'hardware reale (su larga scala).\n",
        "\n",
        "Si tenga presente che ogni grafico relativo a un circuito speculare rappresenta un singolo circuito, non il dato complessivo. L'esempio hardware sopra riportato utilizza un circuito da 26 qubit `tfim` , che rappresenta un caso in cui SABRE produce una profondità a due qubit molto più elevata rispetto al transpiler basato sull'intelligenza artificiale e a Rustiq, pertanto la sua fedeltà è di conseguenza molto inferiore. Questo dato non è rappresentativo dei risultati complessivi: considerando l’insieme completo dei circuiti su larga scala, la profondità a due qubit di SABRE è solitamente vicina a quella del transpiler basato sull’intelligenza artificiale, e i due metodi si distinguono l’uno dall’altro in base a metriche diverse (il transpiler basato sull’intelligenza artificiale per la profondità a due qubit, SABRE per il numero di gate e il tempo di esecuzione). Un singolo risultato \"mirror\" verifica una versione raddoppiata di un circuito anziché l'intero carico di lavoro, pertanto non dovrebbe essere interpretato come un giudizio sulla qualità complessiva del metodo.\n",
        "\n",
        "<span id=\"recommendations\" />\n",
        "\n",
        "### Suggerimenti\n",
        "\n",
        "Non esiste un'unica strategia di transpilazione ottimale valida per tutti i circuiti. La scelta migliore dipende dalla struttura del circuito, dall'obiettivo di ottimizzazione e dal tempo a disposizione per la compilazione:\n",
        "\n",
        "* **SABRE** è l'impostazione predefinita consigliata. È veloce e affidabile e garantisce ottimi risultati su un'ampia varietà di circuiti. Per una messa a punto più approfondita, gli utenti possono aumentare il numero di prove di layout e instradamento (vedere il [tutorial sull’ottimizzazione di SABRE](/docs/tutorials/transpilation-optimizations-with-sabre) ).\n",
        "* Vale la pena provare **il transpiler basato sull'intelligenza artificiale** quando il tempo di compilazione non rappresenta un vincolo, soprattutto quando la priorità è ridurre al minimo la profondità a due qubit: in questo benchmark ha prodotto la profondità a due qubit più bassa sulla maggior parte dei circuiti su larga scala.\n",
        "* **Rustiq** è stato progettato appositamente per `PauliEvolutionGate` i circuiti ed è in grado di individuare soluzioni con profondità molto ridotta e un numero limitato di porte logiche, in particolare nei circuiti di piccole dimensioni. Su circuiti più grandi può talvolta produrre risultati molto più elevati, quindi è preferibile utilizzarlo come uno dei diversi metodi da provare piuttosto che come opzione predefinita.\n",
        "\n",
        "In pratica, l'approccio migliore consiste nell'eseguire tutti i metodi disponibili e selezionare il risultato migliore per ciascun circuito. Il sovraccarico di compilazione derivante dalla sperimentazione di diversi metodi è minimo rispetto al potenziale miglioramento della qualità di esecuzione su hardware reale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sec-next-steps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "Se hai trovato utile questo tutorial, potrebbero interessarti anche i seguenti articoli:\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  * [Ottimizzazioni della transpilazione con SABRE](/docs/tutorials/transpilation-optimizations-with-sabre)\n",
        "  * [Il transpiler basato sull'intelligenza artificiale supera il test](/docs/guides/ai-transpiler-passes)\n",
        "  * [Creare un plugin di transpiler](/docs/guides/create-transpiler-plugin)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sec-references",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Riferimenti\n",
        "\n",
        "\\[1] \"\"LightSABRE: Un algoritmo SABRE leggero e migliorato\". H. Zou, M. Treinish, K. Hartman, A. Ivrii, J. Lishman et al. [https://arxiv.org/abs/2409.08368](https://arxiv.org/abs/2409.08368)\n",
        "\n",
        "\\[2] \"Sintesi e transpiling di circuiti quantistici pratici ed efficienti con Reinforcement Learning\". D. Kremer, V. Villar, H. Paik, I. Duran, I. Faro, J. Cruz-Benito et al. [https://arxiv.org/abs/2405.13196](https://arxiv.org/abs/2405.13196)\n",
        "\n",
        "\\[3] \"Sintesi dei circuiti di rete di Pauli con apprendimento per rinforzo\". R. Dubal, D. Kremer, S. Martiel, V. Villar, D. Wang, J. Cruz-Benito et al. [https://arxiv.org/abs/2503.14448](https://arxiv.org/abs/2503.14448)\n",
        "\n",
        "\\[4] \"Sintesi più rapida e più breve dei circuiti di simulazione hamiltoniana\". T. Goubault de Brugiere, S. Martiel et al. [ https://arxiv.org/abs/2404.03280](https://arxiv.org/abs/2404.03280)\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": 5
}