{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "meta-header",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Introduzione al transpiler basato su Qiskit AI\"\n",
        "description: \"Scopri come il transpiler basato sull'intelligenza artificiale si confronta con la transpilazione standard che utilizza circuiti speculari.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize fontweight steelblue AITI */}\n",
        "\n",
        "<span id=\"qiskit-ai-powered-transpiler-introduction\" />\n",
        "\n",
        "# Introduzione al transpiler basato su Qiskit AI\n",
        "\n",
        "*Stima della durata: 5 minuti su IBM Heron (NOTA: si tratta solo di una stima. (La durata potrebbe variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "learning-outcomes",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Dopo aver seguito questo tutorial, gli utenti dovrebbero aver compreso:\n",
        "\n",
        "* Come utilizzare il transpiler basato sull'intelligenza artificiale (`generate_ai_pass_manager`) come sostituto diretto del transpiler standard\n",
        "* Confronto tra il transpiler basato sull'intelligenza artificiale e quello predefinito in termini di profondità a due qubit, numero di gate e tempo di transpilazione\n",
        "* Come utilizzare i circuiti specchio per valutare la qualità della transpilazione tramite l'esecuzione hardware\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Consigliamo agli utenti di acquisire familiarità con i seguenti argomenti prima di seguire questo tutorial:\n",
        "\n",
        "* [Circuiti Transpile](/docs/guides/transpile)\n",
        "* [Configurare i gestori di pass predefiniti](/docs/guides/transpile-with-pass-managers)\n",
        "* [Il transpiler basato sull'intelligenza artificiale supera il test](/docs/guides/ai-transpiler-passes)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "**Il transpiler basato sull'intelligenza artificiale di** Qiskit introduce passaggi di transpilazione basati sull'apprendimento automatico in grado di produrre circuiti più brevi e più efficienti dal punto di vista hardware rispetto ai metodi euristici tradizionali come SABRE. I circuiti più corti accumulano meno rumore, il che migliora direttamente la qualità dei risultati sull'hardware quantistico reale.\n",
        "\n",
        "In questo tutorial mettiamo a confronto due strategie di transpilazione:\n",
        "\n",
        "| Strategia       | Services                                                                       |\n",
        "| --------------- | ------------------------------------------------------------------------------ |\n",
        "| **Predefinito** | `generate_preset_pass_manager(optimization_level=3, ...)`                      |\n",
        "| **IA**          | `generate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...)` |\n",
        "\n",
        "Per ogni strategia misuriamo tre parametri: **la profondità dei gate a due qubit**, **il numero totale di gate** e **il tempo di esecuzione della transpilazione**.\n",
        "\n",
        "<span id=\"ai-powered-transpiler-benchmarks\" />\n",
        "\n",
        "### Benchmark dei transpiler basati sull'intelligenza artificiale\n",
        "\n",
        "Nei test di benchmark, il transpiler basato sull'intelligenza artificiale ha prodotto costantemente circuiti meno complessi e di qualità superiore rispetto al transpiler standard di Qiskit. Per questi test abbiamo utilizzato la strategia predefinita di gestione dei passaggi di Qiskit, configurata con [`generate_preset_pass_manager`](/docs/api/qiskit/qiskit.transpiler.generate_preset_pass_manager). Sebbene questa strategia predefinita sia spesso efficace, può incontrare difficoltà con circuiti più grandi o più complessi. Al contrario, i passaggi basati sull’intelligenza artificiale hanno consentito una riduzione media del 24% nel numero di gate a due qubit e del 36% nella profondità dei circuiti di grandi dimensioni (oltre 100 qubit) durante la transpilazione nella topologia “heavy-hex” dell’hardware dell’ IBM Quantum®. Per ulteriori informazioni su questi benchmark, consulta questo [blog](https://www.ibm.com/quantum/blog/qiskit-performance).\n",
        "\n",
        "![Benchmark dei transpiler basati sull'intelligenza artificiale](https://quantum.cloud.ibm.com/docs/images/tutorials/ai-transpiler-introduction/ai-transpiler-benchmarks.avif)\n",
        "\n",
        "Questo tutorial illustra i principali vantaggi dei passaggi basati sull'intelligenza artificiale e li mette a confronto con i metodi tradizionali.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.0 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime `pip install qiskit-ibm-runtime`v0.22 o versioni successive\n",
        "* Transpilatore Qiskit IBM con modalità locale AI (`pip install 'qiskit-ibm-transpiler[ai-local-mode]'`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup-header",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "setup-code",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.circuit.random import random_circuit\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "from qiskit_ibm_transpiler import generate_ai_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel, depolarizing_error\n",
        "import matplotlib.pyplot as plt\n",
        "from statistics import mean, stdev\n",
        "import time\n",
        "import logging\n",
        "\n",
        "seed = 42\n",
        "\n",
        "\n",
        "def transpile_with_metrics(pass_manager, circuit):\n",
        "    \"\"\"Transpile a circuit and return the result along with key metrics.\"\"\"\n",
        "    start = time.time()\n",
        "    qc_out = pass_manager.run(circuit)\n",
        "    elapsed = time.time() - start\n",
        "\n",
        "    depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    gate_count = qc_out.size()\n",
        "\n",
        "    return qc_out, {\n",
        "        \"depth_2q\": depth_2q,\n",
        "        \"gate_count\": gate_count,\n",
        "        \"time_s\": round(elapsed, 3),\n",
        "    }\n",
        "\n",
        "\n",
        "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",
        "    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, simulate=True):\n",
        "    \"\"\"Build a mirror circuit: U followed by U-dagger, with measurements.\n",
        "\n",
        "    The expected output is always |0...0>, so measuring the survival\n",
        "    probability reveals how much noise each transpilation strategy adds.\n",
        "\n",
        "    Args:\n",
        "        tqc: A transpiled circuit.\n",
        "        simulate: If True (default), remap to contiguous qubits so Aer\n",
        "            only simulates the active qubits. If False, keep the full\n",
        "            physical layout for hardware execution.\"\"\"\n",
        "    if simulate:\n",
        "        tqc = remap_to_contiguous(tqc)\n",
        "    mirror = tqc.compose(tqc.inverse())\n",
        "    mirror.measure_all()\n",
        "    return mirror\n",
        "\n",
        "\n",
        "def print_summary(results):\n",
        "    \"\"\"Print a summary of each metric as mean +/- stdev across all circuits,\n",
        "    along with the mean percentage improvement of AI over Default.\"\"\"\n",
        "    metrics = [\n",
        "        (\"Depth 2Q\", \"Depth 2Q (Default)\", \"Depth 2Q (AI)\"),\n",
        "        (\"Gate Count\", \"Gate Count (Default)\", \"Gate Count (AI)\"),\n",
        "        (\"Time (s)\", \"Time (Default)\", \"Time (AI)\"),\n",
        "    ]\n",
        "    header = (\n",
        "        f\"{'Metric':<12}{'Default (mean +/- std)':>24}\"\n",
        "        f\"{'AI (mean +/- std)':>22}{'AI % improvement':>22}\"\n",
        "    )\n",
        "    print(header)\n",
        "    print(\"-\" * len(header))\n",
        "    for label, col_def, col_ai in metrics:\n",
        "        defaults = [r[col_def] for r in results]\n",
        "        ais = [r[col_ai] for r in results]\n",
        "        pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]\n",
        "        default_str = f\"{mean(defaults):.1f} +/- {stdev(defaults):.1f}\"\n",
        "        ai_str = f\"{mean(ais):.1f} +/- {stdev(ais):.1f}\"\n",
        "        pct_str = f\"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%\"\n",
        "        print(f\"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}\")\n",
        "\n",
        "\n",
        "def plot_metrics_and_pct(results, title_prefix):\n",
        "    \"\"\"Plot metric comparisons and percentage improvement of AI over Default.\"\"\"\n",
        "    qubits = [r[\"Qubits\"] for r in results]\n",
        "    metrics = [\n",
        "        (\"Depth 2Q (Default)\", \"Depth 2Q (AI)\", \"Two-Qubit Depth\"),\n",
        "        (\"Gate Count (Default)\", \"Gate Count (AI)\", \"Gate Count\"),\n",
        "        (\"Time (Default)\", \"Time (AI)\", \"Transpilation Time\"),\n",
        "    ]\n",
        "\n",
        "    # Row 1: raw metric comparison\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: Metric Comparison\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        ax.plot(qubits, [r[col_def] for r in results], \"o-\", label=\"Default\")\n",
        "        ax.plot(qubits, [r[col_ai] for r in results], \"s-\", label=\"AI\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(label)\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "    # Row 2: percentage improvement\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: % Improvement of AI over Default\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]\n",
        "        ax.axhline(\n",
        "            0, color=\"#1f77b4\", linewidth=2, label=\"Default (baseline)\"\n",
        "        )\n",
        "        ax.plot(qubits, pct, \"s-\", color=\"#ff7f0e\", label=\"AI\")\n",
        "        ax.fill_between(qubits, 0, pct, alpha=0.15, color=\"#ff7f0e\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(\"% Improvement\")\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "# Suppress verbose AI-powered transpiler logs\n",
        "logging.getLogger(\n",
        "    \"qiskit_ibm_transpiler.wrappers.ai_local_synthesis\"\n",
        ").setLevel(logging.WARNING)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-header",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step1-header",
      "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",
        "Generiamo 20 circuiti casuali di profondità 4, in cui il numero di qubit varia da sei a 25. Questi circuiti fungeranno da casi di prova per il confronto tra le diverse strategie di transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "sim-step1-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 20 circuits with qubit counts from 6 to 25\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step1-code-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_circuits_sim = 20\n",
        "depth_sim = 4\n",
        "qubit_range_sim = list(range(6, 26))\n",
        "\n",
        "circuits_sim = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_sim,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_sim)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_sim)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}\"\n",
        ")\n",
        "circuits_sim[0].draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-header",
      "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",
        "Creiamo il gestore di passaggi predefinito (SABRE) per il backend selezionato. Entrambe le strategie di transpilazione prendono di mira la mappa di accoppiamento completa del backend. La simulazione locale rimane gestibile anche in seguito, poiché la fase di simulazione utilizza `remap_to_contiguous` per riassegnare ogni circuito transpilato esclusivamente ai propri qubit attivi; in questo modo, Aer simula solo quei qubit anziché l'intero dispositivo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "sim-step2-backend",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    min_num_qubits=100, operational=True, simulator=False\n",
        ")\n",
        "\n",
        "\n",
        "pm_default_sim = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "sim-step2-transpile",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "bc06de12e386408abf67166416c2d1a0",
              "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": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q               33.0 +/- 12.9          26.4 +/- 8.0      +15.8% +/- 17.6%\n",
            "Gate Count           522.0 +/- 266.0       560.5 +/- 279.1        -9.0% +/- 9.0%\n",
            "Time (s)                 0.0 +/- 0.0           0.2 +/- 0.1    -893.6% +/- 362.9%\n"
          ]
        }
      ],
      "source": [
        "results_sim = []\n",
        "\n",
        "for i, qc in enumerate(circuits_sim):\n",
        "    n = qubit_range_sim[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_sim.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_sim)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-table-note",
      "metadata": {},
      "source": [
        "La tabella riassuntiva mostra la media e la deviazione standard di ciascun parametro per tutti i 20 circuiti, insieme alla percentuale media di miglioramento ottenuta dal transpiler basato sull'intelligenza artificiale rispetto a quello predefinito. I valori positivi indicano che il transpiler basato sull'intelligenza artificiale ha prodotto risultati migliori; i valori negativi indicano che l'impostazione predefinita era migliore.\n",
        "\n",
        "In questo esempio su piccola scala, il transpiler basato sull’intelligenza artificiale raggiunge in media una profondità a due qubit inferiore di circa il 16%, ma a costo di un numero di gate superiore di circa il 9%. Ciò mette in evidenza un compromesso fondamentale nella scelta tra le due strategie: il transpiler basato sull’intelligenza artificiale privilegia la riduzione della profondità (un numero minore di livelli sequenziali di porte a due qubit), mentre il transpiler predefinito (SABRE) privilegia la riduzione al minimo del numero totale di porte (un numero minore di inserimenti di porte SWAP). A seconda dell'applicazione, un parametro potrebbe risultare più importante dell'altro.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "sim-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_sim, \"Small-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-analysis",
      "metadata": {},
      "source": [
        "**Profondità a due qubit:** il transpiler basato sull'intelligenza artificiale produce generalmente circuiti con una profondità a due qubit inferiore. La profondità è uno dei parametri principali che il modello di instradamento basato sull’intelligenza artificiale è stato addestrato a ottimizzare, e il miglioramento è evidente nella maggior parte delle dimensioni dei circuiti, sebbene SABRE riesca a eguagliarlo o a superarlo su singoli circuiti.\n",
        "\n",
        "**Conteggio dei voti:** i risultati sono molto simili su questa scala, con SABRE che mantiene un leggero vantaggio complessivo. L'euristica di instradamento di SABRE è progettata per ridurre al minimo il numero di porte SWAP inserite, il che comporta una riduzione diretta del numero di porte. Quando le dimensioni dei circuiti sono ridotte, la differenza è modesta.\n",
        "\n",
        "**Tempo di transpilazione:** il tempo di esecuzione di SABRE è pressoché costante indipendentemente dal numero di qubit, quindi le dimensioni del circuito hanno un effetto minimo sul tempo di transpilazione a questa scala. La logica di routing principale di SABRE è altamente ottimizzata (implementata in gran parte in Rust). Il transpiler basato sull'intelligenza artificiale richiede un tempo notevolmente più lungo e la durata aumenta in base alle dimensioni del circuito, anche se i tempi assoluti rimangono ragionevoli per un utilizzo interattivo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-header",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n",
        "Per valutare l'impatto della transpilazione sulla fedeltà del circuito, costruire circuiti speculari a partire dal caso a 10 qubit ed eseguirli sul simulatore Aer con un modello di rumore semplice. L'output atteso di un circuito a specchio è sempre una stringa di bit composta interamente da zeri; pertanto, la probabilità di rilevare un $|0\\rangle^{\\otimes n}$ e dimostra in che misura ciascuna strategia di transpilazione preservi la fedeltà.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "sim-step3-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default: depth 84, gates 280\n",
            "AI:      depth 91, gates 343\n"
          ]
        }
      ],
      "source": [
        "# Use the 10-qubit circuit (index where qubits == 10)\n",
        "idx_10q = qubit_range_sim.index(10)\n",
        "\n",
        "qc_10q = circuits_sim[idx_10q]\n",
        "qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)\n",
        "\n",
        "tqc_methods = {\n",
        "    \"Default\": qc_default_10q,\n",
        "    \"AI\": qc_ai_10q,\n",
        "}\n",
        "\n",
        "print(\n",
        "    f\"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}\"\n",
        ")\n",
        "print(f\"AI:      depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "sim-step3-run",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.8460  (8460/10000)\n",
            "AI        P(|00...0>) = 0.8121  (8121/10000)\n"
          ]
        }
      ],
      "source": [
        "# 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",
        "survival_probs = {}\n",
        "\n",
        "for method, tqc in tqc_methods.items():\n",
        "    mirror = build_mirror_circuit(tqc, simulate=True)\n",
        "\n",
        "    sampler = SamplerV2(mode=aer_sim)\n",
        "    job = sampler.run([mirror], shots=shots)\n",
        "    counts = job.result()[0].data.meas.get_counts()\n",
        "\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots\n",
        "    survival_probs[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-analysis",
      "metadata": {},
      "source": [
        "Abbiamo eseguito entrambi i circuiti a specchio nel simulatore Aer utilizzando un semplice modello di rumore depolarizzante. La probabilità di sopravvivenza, definita come la frazione di tentativi che restituiscono la stringa di bit composta interamente da zeri, quantifica la quantità di rumore introdotta da ciascuna strategia di transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-header",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato\n",
        "\n",
        "Calcoliamo la probabilità di ottenere una stringa di bit composta interamente da zeri in entrambe le serie di misurazioni. Una maggiore probabilità di sopravvivenza indica una maggiore fedeltà, il che significa che la trascrizione ha introdotto meno rumore. Il grafico sottostante mostra il complemento, 1 - P(|0...0>), in modo che una barra più bassa indichi una maggiore fedeltà e che le piccole differenze di errore siano più facilmente individuabili.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "sim-step4-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step4-code-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs = {method: 1 - p for method, p in survival_probs.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs.keys(),\n",
        "    error_probs.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(\"Mirror Circuit Error (10-qubit, Aer Simulator)\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-analysis",
      "metadata": {},
      "source": [
        "In questo caso, il transpiler predefinito ha generato un circuito sia meno profondo che più piccolo per questa specifica istanza a 10 qubit, pertanto ci si aspetta che presenti una fedeltà maggiore. I risultati variano a seconda del circuito: come mostra la tabella riassuntiva qui sopra, il vantaggio offerto dal transpiler basato sull’intelligenza artificiale consiste in una profondità media inferiore a due qubit, non in ogni singolo circuito. La strategia che garantisce una maggiore fedeltà dipende dall'entità della differenza in ciascuna metrica, dalle caratteristiche di rumore dell'hardware e dalla struttura del circuito. In un modello di rumore depolarizzante uniforme, il numero totale di gate ha spesso un impatto più diretto sull’errore accumulato rispetto alla sola profondità.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-header",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-steps-header",
      "metadata": {},
      "source": [
        "<span id=\"steps-1-4\" />\n",
        "\n",
        "### Passaggi da 1 a 4\n",
        "\n",
        "Qui tutti questi dettagli vengono integrati in un flusso di lavoro chiaro su scala più ampia, che viene poi eseguito su hardware quantistico reale.\n",
        "\n",
        "Il codice riportato di seguito genera 25 circuiti casuali con profondità 8, in cui il numero di qubit varia da 26 a 50. Questi circuiti vengono quindi transpilati utilizzando entrambe le strategie e vengono raccolte le stesse metriche. Quindi costruiamo circuiti speculari partendo dal caso a 26 qubit e li inviamo al backend reale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "hw-step1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 25 circuits with qubit counts from 26 to 50\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "num_circuits_hw = 25\n",
        "depth_hw = 8\n",
        "qubit_range_hw = list(range(26, 51))\n",
        "\n",
        "circuits_hw = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_hw,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_hw)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_hw)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "hw-step2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q              217.4 +/- 50.4        191.0 +/- 35.6      +10.9% +/- 10.7%\n",
            "Gate Count         4513.3 +/- 1394.3     5227.1 +/- 1536.4       -16.4% +/- 5.8%\n",
            "Time (s)                 0.1 +/- 0.0           3.5 +/- 1.5   -3588.2% +/- 643.6%\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 2-------------------------\n",
        "pm_default = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")\n",
        "\n",
        "results_hw = []\n",
        "\n",
        "for i, qc in enumerate(circuits_hw):\n",
        "    n = qubit_range_hw[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_hw.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_hw)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "hw-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_hw, \"Large-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "hw-step3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mirror circuit (Default): depth 1577, gates 9672\n",
            "Mirror circuit (AI):      depth 1235, gates 11092\n",
            "Job submitted: d8gt7vm6983c73dqbg0g\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 3-------------------------\n",
        "# Build mirror circuits from the 26-qubit case\n",
        "idx_26q = qubit_range_hw.index(26)\n",
        "\n",
        "qc_26q = circuits_hw[idx_26q]\n",
        "qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)\n",
        "\n",
        "mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)\n",
        "mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)\n",
        "\n",
        "# Re-transpile to basis gates (the inverse can introduce gates like sxdg)\n",
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=0,\n",
        "    backend=backend,\n",
        ")\n",
        "mirror_default_hw = pm_basis.run(mirror_default_hw)\n",
        "mirror_ai_hw = pm_basis.run(mirror_ai_hw)\n",
        "\n",
        "print(\n",
        "    f\"Mirror circuit (Default): depth {mirror_default_hw.depth()}, \"\n",
        "    f\"gates {mirror_default_hw.size()}\"\n",
        ")\n",
        "print(\n",
        "    f\"Mirror circuit (AI):      depth {mirror_ai_hw.depth()}, \"\n",
        "    f\"gates {mirror_ai_hw.size()}\"\n",
        ")\n",
        "\n",
        "# Submit to real hardware\n",
        "sampler_hw = SamplerV2(mode=backend)\n",
        "sampler_hw.options.environment.job_tags = [\"TUT_AITI\"]\n",
        "\n",
        "shots_hw = 500000\n",
        "job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)\n",
        "print(f\"Job submitted: {job_hw.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "hw-step4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.0005  (239/500000)\n",
            "AI        P(|00...0>) = 0.0050  (2516/500000)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "result_hw = job_hw.result()\n",
        "\n",
        "survival_probs_hw = {}\n",
        "for i, method in enumerate([\"Default\", \"AI\"]):\n",
        "    counts = result_hw[i].data.meas.get_counts()\n",
        "    mirror = [mirror_default_hw, mirror_ai_hw][i]\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots_hw\n",
        "    survival_probs_hw[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots_hw})\"\n",
        "    )\n",
        "\n",
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs_hw.keys(),\n",
        "    error_probs_hw.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(f\"Mirror Circuit Error (26-qubit, {backend.name})\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-analysis",
      "metadata": {},
      "source": [
        "<span id=\"analysis-of-results\" />\n",
        "\n",
        "### Analisi dei risultati\n",
        "\n",
        "I risultati su larga scala confermano le tendenze osservate nell'esempio su piccola scala, ora in un contesto più impegnativo.\n",
        "\n",
        "**Profondità a due qubit:** il transpiler basato sull’intelligenza artificiale continua a garantire una profondità a due qubit notevolmente inferiore su tutta la gamma di dimensioni dei circuiti. L'ottimizzazione della profondità è uno degli obiettivi principali su cui è addestrato il modello di routing basato sull'intelligenza artificiale, e il vantaggio risulta più evidente con un numero maggiore di qubit, quando il problema di routing diventa più complesso per i metodi euristici.\n",
        "\n",
        "**Numero di porte:** il transpiler predefinito (SABRE) produce costantemente circuiti con un numero inferiore di porte per tutte le dimensioni dei circuiti comprese in questo intervallo. L'euristica di SABRE è stata progettata specificatamente per ridurre al minimo il numero di porte logiche e, su questa scala, il vantaggio è evidente e uniforme.\n",
        "\n",
        "**Tempo di traspilazione:** il divario nel tempo di traspilazione aumenta alle scale più grandi. Il tempo di esecuzione di SABRE rimane pressoché costante, mentre quello del transpiler basato sull'intelligenza artificiale cresce in modo più marcato. Ciononostante, il runtime del transpiler basato sull'intelligenza artificiale rimane una soluzione pratica per la maggior parte dei flussi di lavoro.\n",
        "\n",
        "**Fidelità del circuito a specchio:** entrambi i metodi producono probabilità di sopravvivenza ben inferiori all'1% a questa scala, lasciando poco segnale utilizzabile. Con un numero totale di porte pari a circa 10.000 e una profondità a due qubit superiore a 1.000, il rumore di depolarizzazione accumulato nel circuito a specchio sovrasta la maggior parte del segnale. Ciò evidenzia un limite fondamentale dell'approccio basato sui circuiti a specchio: sebbene sia semplice e non richieda alcuna simulazione classica, non si adatta bene a circuiti di grandi dimensioni o profondi, dove entrambi i metodi vengono spinti vicino alla soglia di rumore e il debole segnale residuo è dominato dall'errore accumulato.\n",
        "\n",
        "Sebbene questi risultati mettano in evidenza l'efficacia del transpiler basato sull'intelligenza artificiale, è importante sottolinearne i limiti. Il metodo di sintesi basato sull'intelligenza artificiale è attualmente disponibile solo per alcune mappe di accoppiamento, il che potrebbe limitarne l'applicabilità su più ampia scala. È opportuno tenere conto di questa limitazione nel valutare il suo impiego in diversi scenari.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "next-steps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti contenuti:\n",
        "\n",
        "  * [Ottimizzazioni della transpilazione con SABRE](/docs/tutorials/transpilation-optimizations-with-sabre)\n",
        "  * [Metodi di compilazione per circuiti di simulazione hamiltoniani](/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits)\n",
        "</Admonition>\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": 300
  },
  "nbformat": 4,
  "nbformat_minor": 5
}