{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "f69d5853-e815-4754-894d-833017217572",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Benchmarking in tempo reale per la selezione dei qubit\"\n",
        "description: \"Esegui esperimenti di caratterizzazione in tempo reale e aggiorna le proprietà del backend per migliorare la selezione dei qubit durante la mappatura di un circuito sui qubit fisici su una QPU.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore sharex */}\n",
        "\n",
        "<span id=\"real-time-benchmarking-for-qubit-selection\" />\n",
        "\n",
        "# Benchmarking in tempo reale per la selezione dei qubit\n",
        "\n",
        "*Stima dell'utilizzo: 4 minuti con un processore Eagle r2 (NOTA: questa è solo una stima. Il tempo di esecuzione potrebbe variare)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "500dc8c9-a5d8-4ef1-932f-30e400d6bdde",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "Questa esercitazione mostra come eseguire esperimenti di caratterizzazione in tempo reale e aggiornare le proprietà del backend per migliorare la selezione dei qubit durante la mappatura di un circuito sui qubit fisici di una QPU. Imparerete gli esperimenti di caratterizzazione di base utilizzati per determinare le proprietà della QPU, come eseguirli in Qiskit e come aggiornare le proprietà salvate nell'oggetto backend che rappresenta la QPU in base a questi esperimenti.\n",
        "\n",
        "Le proprietà riportate dal QPU vengono aggiornate una volta al giorno, ma il sistema può andare alla deriva più velocemente del tempo che intercorre tra un aggiornamento e l'altro. Questo può influire sull'affidabilità delle routine di selezione dei qubit nella fase `Layout` del gestore di passaggi, poiché utilizzerebbero proprietà riportate che non rappresentano lo stato attuale della QPU. Per questo motivo, può valere la pena di dedicare un po' di tempo alla QPU per effettuare esperimenti di caratterizzazione, che possono poi essere utilizzati per aggiornare le proprietà della QPU utilizzate dalla routine `Layout` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0babd413-d91f-4fd7-a0f5-bb46ae0bbf5b",
      "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.40 o più tardi ( `pip install qiskit-ibm-runtime` )\n",
        "* Esperimenti Qiskit v0.12 o successivo ( `pip install qiskit-experiments` )\n",
        "* Libreria di grafi Rustworkx (`pip install rustworkx`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3df52d5f-806a-4846-849e-633706a96d0b",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "4766c18a-ba45-456b-8b78-6b6f1d214586",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import hellinger_fidelity\n",
        "from qiskit.transpiler import InstructionProperties\n",
        "\n",
        "\n",
        "from qiskit_experiments.library import (\n",
        "    T1,\n",
        "    T2Hahn,\n",
        "    LocalReadoutError,\n",
        "    StandardRB,\n",
        ")\n",
        "from qiskit_experiments.framework import BatchExperiment, ParallelExperiment\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Session\n",
        "\n",
        "from datetime import datetime\n",
        "from collections import defaultdict\n",
        "import numpy as np\n",
        "import rustworkx\n",
        "import matplotlib.pyplot as plt\n",
        "import copy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65d49ed2-0581-486e-9031-a08fa9bace92",
      "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",
        "Per valutare la differenza di prestazioni, consideriamo un circuito che prepara uno stato Bell attraverso una catena lineare di lunghezza variabile. Si misura la fedeltà dello stato di Bell alle estremità della catena.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "64c25da9-a728-4ae4-a377-3078a1dc618d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "ideal_dist = {\"00\": 0.5, \"11\": 0.5}\n",
        "\n",
        "num_qubits_list = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 127]\n",
        "circuits = []\n",
        "for num_qubits in num_qubits_list:\n",
        "    circuit = QuantumCircuit(num_qubits, 2)\n",
        "    circuit.h(0)\n",
        "    for i in range(num_qubits - 1):\n",
        "        circuit.cx(i, i + 1)\n",
        "    circuit.barrier()\n",
        "    circuit.measure(0, 0)\n",
        "    circuit.measure(num_qubits - 1, 1)\n",
        "    circuits.append(circuit)\n",
        "\n",
        "circuits[-1].draw(output=\"mpl\", style=\"clifford\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16948f21-a39b-4444-bf02-5f81331825c4",
      "metadata": {},
      "source": [
        "<span id=\"set-up-backend-and-coupling-map\" />\n",
        "\n",
        "### Configurazione del backend e della mappa di accoppiamento\n",
        "\n",
        "Per prima cosa, selezionare un backend\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "f968acca-9131-4f5d-aa74-70befcdda4f5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "\n",
        "qubits = list(range(backend.num_qubits))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90b0ddab-0d35-4784-8575-fe0505699197",
      "metadata": {},
      "source": [
        "Si ottiene quindi la sua mappa di accoppiamento\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "86d84baa-bd5e-4faf-9582-4568ab4c4a4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "\n",
        "# Get unidirectional coupling map\n",
        "one_dir_coupling_map = coupling_graph.edge_list()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d48b5ba2-74ec-4484-9ba6-b0681ae32919",
      "metadata": {},
      "source": [
        "Per poter effettuare il benchmark del maggior numero possibile di porte a due qubit contemporaneamente, separiamo la mappa di accoppiamento in una `layered_coupling_map`. Questo oggetto contiene un elenco di strati, dove ogni strato è un elenco di bordi su cui possono essere eseguite contemporaneamente porte a due qubit. Questa è anche chiamata colorazione dei bordi della mappa di accoppiamento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "62b36ded-ab4e-414e-b146-ff522786a871",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Get layered coupling map\n",
        "edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)\n",
        "layered_coupling_map = defaultdict(list)\n",
        "for edge_idx, color in edge_coloring.items():\n",
        "    layered_coupling_map[color].append(\n",
        "        coupling_graph.get_edge_endpoints_by_index(edge_idx)\n",
        "    )\n",
        "layered_coupling_map = [\n",
        "    sorted(layered_coupling_map[i])\n",
        "    for i in sorted(layered_coupling_map.keys())\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "875117af-8a2c-4aea-92d9-ffeee7ff37d5",
      "metadata": {},
      "source": [
        "<span id=\"characterization-experiments\" />\n",
        "\n",
        "### Esperimenti di caratterizzazione\n",
        "\n",
        "Una serie di esperimenti viene utilizzata per caratterizzare le principali proprietà dei qubit in una QPU. Questi sono $T_1$, $T_2$, l'errore di lettura e l'errore di gate a singolo e a due bit. Riassumeremo brevemente quali sono queste proprietà e faremo riferimento agli esperimenti nel pacchetto [`qiskit-experiments`](https://qiskit-community.github.io/qiskit-experiments/index.html) utilizzati per caratterizzarle.\n",
        "\n",
        "<span id=\"t1\" />\n",
        "\n",
        "#### T1\n",
        "\n",
        "$T_1$ è il tempo caratteristico che impiega un qubit eccitato a cadere allo stato terreno a causa dei processi di decoerenza con smorzamento dell'ampiezza. In un [esperimento $T_1$](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t1.html), misuriamo un qubit eccitato dopo un ritardo. Quanto più grande è il tempo di ritardo, tanto più probabilità che il qubit cada allo stato di massa. L'obiettivo dell'esperimento è quello di caratterizzare il tasso di decadimento del qubit verso lo stato di terra stato fondamentale.\n",
        "\n",
        "<span id=\"t2\" />\n",
        "\n",
        "#### T2\n",
        "\n",
        "$T_2$ rappresenta la quantità di tempo necessaria affinché la proiezione del vettore Bloch di un singolo qubit sul piano XY scenda a circa il 37% ( $\\frac{1}{e}$ ) della sua ampiezza iniziale a causa dei processi di decoerenza con dephasing della sua ampiezza iniziale a causa dei processi di decoerenza con dephasing. In un [esperimento di eco di Hahn $T_2$](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t2hahn.html), possiamo stimare la velocità di questo decadimento.\n",
        "\n",
        "<span id=\"state-preparation-and-measurement-spam-error-characterization\" />\n",
        "\n",
        "#### Caratterizzazione dell'errore di preparazione e misurazione dello stato (SPAM)\n",
        "\n",
        "In un [esperimento di caratterizzazione degli errori SPAM](https://qiskit-community.github.io/qiskit-experiments/manuals/measurement/readout_mitigation.html), i qubit vengono preparati in un determinato stato ( $\\vert 0 \\rangle$ o $\\vert 1 \\rangle$ ) e misurati. La probabilità di misurare uno stato diverso da quello preparato dà quindi la probabilità dell'errore.\n",
        "\n",
        "<span id=\"single-qubit-and-two-qubit-randomized-benchmarking\" />\n",
        "\n",
        "#### Benchmarking randomizzato a singolo qubit e a due qubit\n",
        "\n",
        "[Il benchmarking randomizzato (RB)](https://qiskit-community.github.io/qiskit-experiments/manuals/verification/randomized_benchmarking.html) è un protocollo popolare per caratterizzare il tasso di errore dei processori quantistici processori quantistici. Un esperimento di RB consiste nella generazione di circuiti Clifford sui qubit dati, in modo che l'unitario calcolato dai circuiti sia l'identità identità. Dopo aver eseguito i circuiti, si conta il numero di scatti che danno luogo a un errore (cioè un'uscita diversa dallo stato fondamentale) e da questi dati si possono dedurre le stime di errore per il dispositivo quantistico, calcolando l'Errore per Clifford.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "9d57c42d-7a91-4e79-bc6c-6e579da929f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create T1 experiments on all qubit in parallel\n",
        "t1_exp = ParallelExperiment(\n",
        "    [\n",
        "        T1(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create T2-Hahn experiments on all qubit in parallel\n",
        "t2_exp = ParallelExperiment(\n",
        "    [\n",
        "        T2Hahn(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create readout experiments on all qubit in parallel\n",
        "readout_exp = LocalReadoutError(qubits)\n",
        "\n",
        "# Create single-qubit RB experiments on all qubit in parallel\n",
        "singleq_rb_exp = ParallelExperiment(\n",
        "    [\n",
        "        StandardRB(\n",
        "            physical_qubits=[qubit], lengths=[10, 100, 500], num_samples=10\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create two-qubit RB experiments on the three layers of disjoint edges of the heavy-hex\n",
        "twoq_rb_exp_batched = BatchExperiment(\n",
        "    [\n",
        "        ParallelExperiment(\n",
        "            [\n",
        "                StandardRB(\n",
        "                    physical_qubits=pair,\n",
        "                    lengths=[10, 50, 100],\n",
        "                    num_samples=10,\n",
        "                )\n",
        "                for pair in layer\n",
        "            ],\n",
        "            backend,\n",
        "            analysis=None,\n",
        "        )\n",
        "        for layer in layered_coupling_map\n",
        "    ],\n",
        "    backend,\n",
        "    flatten_results=True,\n",
        "    analysis=None,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cad4f8d3-c2d5-4bb5-92be-432a4573e14a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-properties-over-time\" />\n",
        "\n",
        "### Proprietà QPU nel tempo\n",
        "\n",
        "Osservando le proprietà QPU riportate nel corso del tempo (di seguito prenderemo in considerazione una singola settimana), vediamo come queste possano fluttuare su una scala di un singolo giorno. Piccole fluttuazioni possono verificarsi anche nell'arco di un giorno. In questo scenario, le proprietà segnalate (aggiornate una volta al giorno) non cattureranno accuratamente lo stato attuale del QPU. Inoltre, se un lavoro viene transpilato localmente (utilizzando le proprietà correnti riportate) e inviato ma eseguito solo in un momento successivo (minuti o giorni), può correre il rischio di aver utilizzato proprietà obsolete per la selezione dei qubit nella fase di transpilatura. Ciò evidenzia l'importanza di disporre di informazioni aggiornate sulla QPU al momento dell'esecuzione. Per prima cosa, recuperiamo le proprietà in un determinato intervallo di tempo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "af1b6722-e77b-436a-bbcd-f272b95bb28c",
      "metadata": {},
      "outputs": [],
      "source": [
        "instruction_2q_name = \"cz\"  # set the name of the default 2q of the device\n",
        "errors_list = []\n",
        "for day_idx in range(10, 17):\n",
        "    calibrations_time = datetime(\n",
        "        year=2025, month=8, day=day_idx, hour=0, minute=0, second=0\n",
        "    )\n",
        "    targer_hist = backend.target_history(datetime=calibrations_time)\n",
        "\n",
        "    t1_dict, t2_dict = {}, {}\n",
        "    for qubit in range(targer_hist.num_qubits):\n",
        "        t1_dict[qubit] = targer_hist.qubit_properties[qubit].t1\n",
        "        t2_dict[qubit] = targer_hist.qubit_properties[qubit].t2\n",
        "\n",
        "    errors_dict = {\n",
        "        \"1q\": targer_hist[\"sx\"],\n",
        "        \"2q\": targer_hist[f\"{instruction_2q_name}\"],\n",
        "        \"spam\": targer_hist[\"measure\"],\n",
        "        \"t1\": t1_dict,\n",
        "        \"t2\": t2_dict,\n",
        "    }\n",
        "\n",
        "    errors_list.append(errors_dict)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac75df1b-f689-475c-94a2-d70d85b1f8ca",
      "metadata": {},
      "source": [
        "Quindi, tracciamo i valori\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e0ba509d-e0e0-438b-aedf-5e01919c7d4f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/e0ba509d-e0e0-438b-aedf-5e01919c7d4f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axs = plt.subplots(5, 1, figsize=(10, 20), sharex=False)\n",
        "\n",
        "\n",
        "# Plot for T1 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t1s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t1_dict = errors_dict[\"t1\"]\n",
        "        try:\n",
        "            t1s.append(t1_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t1 data for qubit {qubit}\")\n",
        "\n",
        "    axs[0].plot(t1s)\n",
        "\n",
        "axs[0].set_title(\"T1\")\n",
        "axs[0].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[0].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot for T2 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t2s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t2_dict = errors_dict[\"t2\"]\n",
        "        try:\n",
        "            t2s.append(t2_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t2 data for qubit {qubit}\")\n",
        "\n",
        "    axs[1].plot(t2s)\n",
        "\n",
        "axs[1].set_title(\"T2\")\n",
        "axs[1].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[1].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot SPAM values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    spams = []\n",
        "    for errors_dict in errors_list:\n",
        "        spam_dict = errors_dict[\"spam\"]\n",
        "        spams.append(spam_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[2].plot(spams)\n",
        "\n",
        "axs[2].set_title(\"SPAM Errors\")\n",
        "axs[2].set_ylabel(\"Error Rate\")\n",
        "axs[2].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 1Q Gate Errors\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    oneq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        oneq_gate_dict = errors_dict[\"1q\"]\n",
        "        oneq_gates.append(oneq_gate_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[3].plot(oneq_gates)\n",
        "\n",
        "axs[3].set_title(\"1Q Gate Errors\")\n",
        "axs[3].set_ylabel(\"Error Rate\")\n",
        "axs[3].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 2Q Gate Errors\n",
        "for pair in one_dir_coupling_map:\n",
        "    twoq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        twoq_gate_dict = errors_dict[\"2q\"]\n",
        "        twoq_gates.append(twoq_gate_dict[pair].error)\n",
        "\n",
        "    axs[4].plot(twoq_gates)\n",
        "\n",
        "axs[4].set_title(\"2Q Gate Errors\")\n",
        "axs[4].set_ylabel(\"Error Rate\")\n",
        "axs[4].set_xlabel(\"Days\")\n",
        "\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3343934-9273-457c-900b-2004b3aa9d0c",
      "metadata": {},
      "source": [
        "Si può notare che nell'arco di diversi giorni alcune proprietà dei qubit possono cambiare considerevolmente. Ciò evidenzia l'importanza di avere informazioni fresche sullo stato della QPU, per poter selezionare i qubit più performanti per un esperimento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e53f0a5-7713-4915-ae47-c1aa0a4f17cd",
      "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",
        "In questa esercitazione non viene effettuata alcuna ottimizzazione dei circuiti o degli operatori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "862789c2-d17e-43ed-8e1b-2f37d7628ed2",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d88f925f-3ffd-43de-ac1f-2c5ba3b8cd96",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-default-qubit-selection\" />\n",
        "\n",
        "### Eseguire un circuito quantistico con selezione predefinita dei qubit\n",
        "\n",
        "Come risultato di riferimento per le prestazioni, eseguiremo un circuito quantistico su una QPU utilizzando i qubit di default, ovvero i qubit selezionati con le proprietà del backend richieste. Utilizzeremo `optimization_level = 3`.  Questa impostazione include l'ottimizzazione più avanzata della transpilazione e utilizza le proprietà del target (come gli errori di operazione) per selezionare i qubit più performanti per l'esecuzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c0d09ad-2e6a-4067-8034-8df92a475ff1",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits)\n",
        "initial_qubits = [\n",
        "    [\n",
        "        idx\n",
        "        for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "        if qb._register.name != \"ancilla\"\n",
        "    ]\n",
        "    for circuit in isa_circuits\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d4fb04c-a5cf-48e8-a759-dde64fd751ac",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-real-time-qubit-selection\" />\n",
        "\n",
        "### Eseguire un circuito quantistico con selezione dei qubit in tempo reale\n",
        "\n",
        "In questa sezione analizzeremo l'importanza di avere informazioni aggiornate sulle proprietà dei qubit della QPU per ottenere risultati ottimali. Per prima cosa, effettueremo una serie completa di esperimenti di caratterizzazione delle QPU ( $T_1$, $T_2$, SPAM, RB a un solo qubit e RB a due qubit), che potremo poi utilizzare per aggiornare le proprietà del backend. Ciò consente al gestore del passaggio di selezionare i qubit per l'esecuzione in base a nuove informazioni sulla QPU, migliorando eventualmente le prestazioni dell'esecuzione. In secondo luogo, eseguiamo il circuito a coppie di Bell e confrontiamo la fedeltà ottenuta dopo aver selezionato i qubit con le proprietà aggiornate della QPU con la fedeltà ottenuta in precedenza quando si utilizzano le proprietà riportate di default per la selezione dei qubit.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  Si noti che alcuni degli esperimenti di caratterizzazione potrebbero fallire quando la routine di adattamento non riesce ad adattare una curva ai dati misurati. Se vedete avvisi provenienti da questi esperimenti, ispezionateli per capire quale caratterizzazione è fallita su quali qubit e provate a regolare i parametri dell'esperimento (come i tempi per $T_1$, $T_2$, o il numero di lunghezze degli esperimenti RB).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "a8266467-9d60-411f-89dd-8cad6558f588",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Prepare characterization experiments\n",
        "batches = [t1_exp, t2_exp, readout_exp, singleq_rb_exp, twoq_rb_exp_batched]\n",
        "batches_exp = BatchExperiment(batches, backend)  # , analysis=None)\n",
        "run_options = {\"shots\": 1e3, \"dynamic\": False}\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    sampler = SamplerV2(mode=session)\n",
        "\n",
        "    # Run characterization experiments\n",
        "    batches_exp_data = batches_exp.run(\n",
        "        sampler=sampler, **run_options\n",
        "    ).block_for_results()\n",
        "\n",
        "    EPG_sx_result_list = batches_exp_data.analysis_results(\"EPG_sx\")\n",
        "    EPG_sx_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_sx_result_list\n",
        "    ]\n",
        "    EPG_x_result_list = batches_exp_data.analysis_results(\"EPG_x\")\n",
        "    EPG_x_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_x_result_list\n",
        "    ]\n",
        "    T1_result_list = batches_exp_data.analysis_results(\"T1\")\n",
        "    T1_result_q_indices = [\n",
        "        result.device_components.index for result in T1_result_list\n",
        "    ]\n",
        "\n",
        "    T2_result_list = batches_exp_data.analysis_results(\"T2\")\n",
        "    T2_result_q_indices = [\n",
        "        result.device_components.index for result in T2_result_list\n",
        "    ]\n",
        "\n",
        "    Readout_result_list = batches_exp_data.analysis_results(\n",
        "        \"Local Readout Mitigator\"\n",
        "    )\n",
        "\n",
        "    EPG_2q_result_list = batches_exp_data.analysis_results(\n",
        "        f\"EPG_{instruction_2q_name}\"\n",
        "    )\n",
        "\n",
        "    # Update target properties\n",
        "    target = copy.deepcopy(backend.target)\n",
        "    for i in range(target.num_qubits - 1):\n",
        "        qarg = (i,)\n",
        "\n",
        "        if qarg in EPG_sx_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"sx\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_sx_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        if qarg in EPG_x_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"x\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_x_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "        err_mat = Readout_result_list.value.assignment_matrix(i)\n",
        "        readout_assignment_error = (\n",
        "            err_mat[0, 1] + err_mat[1, 0]\n",
        "        ) / 2  # average readout error\n",
        "        target.update_instruction_properties(\n",
        "            instruction=\"measure\",\n",
        "            qargs=qarg,\n",
        "            properties=InstructionProperties(error=readout_assignment_error),\n",
        "        )\n",
        "\n",
        "        if qarg in T1_result_q_indices:\n",
        "            target.qubit_properties[i].t1 = T1_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "        if qarg in T2_result_q_indices:\n",
        "            target.qubit_properties[i].t2 = T2_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "\n",
        "    for pair_idx, pair in enumerate(one_dir_coupling_map):\n",
        "        qarg = tuple(pair)\n",
        "        try:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        except:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg[::-1],\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "    # transpile circuits to updated target\n",
        "    pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "    isa_circuit_updated = pm.run(circuits)\n",
        "    updated_qubits = [\n",
        "        [\n",
        "            idx\n",
        "            for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "            if qb._register.name != \"ancilla\"\n",
        "        ]\n",
        "        for circuit in isa_circuit_updated\n",
        "    ]\n",
        "\n",
        "    n_trials = 3  # run multiple trials to see variations\n",
        "\n",
        "    # interleave circuits\n",
        "    interleaved_circuits = []\n",
        "    for original_circuit, updated_circuit in zip(\n",
        "        isa_circuits, isa_circuit_updated\n",
        "    ):\n",
        "        interleaved_circuits.append(original_circuit)\n",
        "        interleaved_circuits.append(updated_circuit)\n",
        "\n",
        "    # Run circuits\n",
        "    # Set simple error suppression/mitigation options\n",
        "    sampler.options.dynamical_decoupling.enable = True\n",
        "    sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "\n",
        "    job_interleaved = sampler.run(interleaved_circuits * n_trials)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d72e021a-3ea6-4ed2-829f-e59ce8017ef4",
      "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",
        "Infine, confrontiamo la fedeltà dello stato di Bell ottenuto nelle due diverse impostazioni:\n",
        "\n",
        "* `original`cioè con i qubit predefiniti scelti dal transpiler in base alle proprietà segnalate del backend.\n",
        "* `updated`cioè con i qubit scelti in base alle proprietà aggiornate del backend dopo l'esecuzione degli esperimenti di caratterizzazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "5a19a5d1-5daf-4d65-9785-8f8724853821",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job_interleaved.result()\n",
        "all_fidelity_list, all_fidelity_updated_list = [], []\n",
        "for exp_idx in range(n_trials):\n",
        "    fidelity_list, fidelity_updated_list = [], []\n",
        "\n",
        "    for idx, num_qubits in enumerate(num_qubits_list):\n",
        "        pub_result_original = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx\n",
        "        ]\n",
        "        pub_result_updated = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx + 1\n",
        "        ]\n",
        "\n",
        "        fid = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_original.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_list.append(fid)\n",
        "\n",
        "        fid_up = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_updated.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_updated_list.append(fid_up)\n",
        "    all_fidelity_list.append(fidelity_list)\n",
        "    all_fidelity_updated_list.append(fidelity_updated_list)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "656ec97a-3fd9-4635-9a98-1c5589761689",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/656ec97a-3fd9-4635-9a98-1c5589761689-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(8, 6))\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"original\",\n",
        "    color=\"b\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_list, '-.')\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_updated_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_updated_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"updated\",\n",
        "    color=\"r\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_updated_list, '-.')\n",
        "plt.xlabel(\"Chain length\")\n",
        "plt.xticks(num_qubits_list)\n",
        "plt.ylabel(\"Fidelity\")\n",
        "plt.title(\"Bell pair fidelity at the edge of N-qubits chain\")\n",
        "plt.legend()\n",
        "plt.grid(\n",
        "    alpha=0.2,\n",
        "    linestyle=\"-.\",\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3705d85c-e11d-4a8f-bfae-20bd501124d8",
      "metadata": {},
      "source": [
        "Non tutte le esecuzioni mostreranno un miglioramento delle prestazioni grazie alla caratterizzazione in tempo reale e con l'aumento della lunghezza della catena, e quindi con una minore libertà di scelta dei qubit fisici, l'importanza delle informazioni aggiornate sul dispositivo diventa meno sostanziale. Tuttavia, è buona norma raccogliere nuovi dati sulle proprietà del dispositivo per comprenderne le prestazioni. Occasionalmente, i sistemi transitori a due livelli possono influenzare le prestazioni di alcuni qubit. I dati in tempo reale possono informarci quando si verificano tali eventi e aiutarci a evitare fallimenti sperimentali in questi casi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd81b208-b13b-4988-854e-1741408f36f3",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Invito all'azione\">\n",
        "  Provate ad applicare questo metodo alle vostre esecuzioni e verificate quanti vantaggi ottenete! Si può anche provare a vedere i miglioramenti ottenuti da diversi backend.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b94755c1-1c19-434a-96b9-b83922b5d63c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Sondaggio tutorial\n",
        "\n",
        "Vi invitiamo a partecipare a questo breve sondaggio per fornire un feedback su questa esercitazione. Le vostre indicazioni ci aiuteranno a migliorare la nostra offerta di contenuti e l'esperienza degli utenti.\n",
        "\n",
        "[Collegamento al sondaggio](https://your.feedback.ibm.com/jfe/form/SV_0w6FZ9QrWkKfTQq)\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"
    },
    "vscode": {
      "interpreter": {
        "hash": "72460b19b35c51a2148375679c8e282bc3cd3c5550c209f0a38b7d09cded82d9"
      }
    },
    "hours": 2.5,
    "qpuSeconds": 240
  },
  "nbformat": 4,
  "nbformat_minor": 4
}