{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "44ef87b3",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simulazione dell'Hamiltoniano di Ising con circuiti dinamici\"\n",
        "description: \"Tutorial che illustra i circuiti dinamici su scala industriale utilizzando una simulazione del modello Ising esagonale con kick\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore hcords ycords xcords fontsize ncol Krsulich Lishman */}\n",
        "\n",
        "<span id=\"simulation-of-kicked-ising-hamiltonian-with-dynamic-circuits\" />\n",
        "\n",
        "# Simulazione dell'Hamiltoniano di Ising con circuiti dinamici\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2ae52bd4",
      "metadata": {},
      "source": [
        "*Stima di utilizzo: 7.5 minuti su un processore Heron r3. (NOTA: Si tratta solo di una stima. Il tempo di esecuzione può variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c76f2627",
      "metadata": {},
      "source": [
        "I circuiti dinamici sono circuiti con feedforward classico, ovvero misurazioni effettuate a metà circuito seguite da operazioni logiche classiche che determinano operazioni quantistiche condizionate dall'output classico. In questo tutorial, simuliamo il modello di Ising con kick su un reticolo esagonale di spin e utilizziamo circuiti dinamici per realizzare interazioni che vanno oltre la connettività fisica dell'hardware.\n",
        "\n",
        "Il modello di Ising è stato studiato approfonditamente in diversi ambiti della fisica. Modella gli spin che subiscono interazioni di Ising tra i siti del reticolo, nonché gli impulsi provenienti dal campo magnetico locale su ciascun sito. L'evoluzione temporale trotterizzata degli spin considerati in questo tutorial, tratta da [\\[1\\]](#references), è data dalla seguente unità:\n",
        "\n",
        "$$\n",
        "U(\\theta)=\\left(\\prod_{\\langle j, k\\rangle} \\exp \\left(i \\frac{\\pi}{8} Z_j Z_k\\right)\\right)\\left(\\prod_j \\exp \\left(-i \\frac{\\theta}{2} X_j\\right)\\right)\n",
        "$$\n",
        "\n",
        "Per sondare la dinamica degli spin, studiamo la magnetizzazione media degli spin in ciascun sito in funzione dei passi di Trotter. Quindi, costruiamo la seguente osservabile:\n",
        "\n",
        "$$\n",
        "\\langle O\\rangle =  \\frac{1}{N} \\sum_i \\langle Z_i \\rangle\n",
        "$$\n",
        "\n",
        "Per realizzare l'interazione ZZ tra i siti del reticolo, presentiamo una soluzione che utilizza la funzione di circuito dinamico, che porta a una profondità di due qubit significativamente più breve rispetto al metodo di instradamento standard con porte SWAP. D'altra parte, le operazioni feedforward classiche nei circuiti dinamici hanno tipicamente tempi di esecuzione più lunghi rispetto ai gate quantistici; pertanto, i circuiti dinamici presentano limitazioni e compromessi. Presentiamo inoltre un modo per aggiungere una sequenza di disaccoppiamento dinamico sui qubit inattivi durante l'operazione di feedforward classica utilizzando la durata [dell'allungamento](/docs/guides/stretch).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a6c0af3b",
      "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 v0.37 o versioni successive con supporto alla visualizzazione (`pip install 'qiskit-ibm-runtime[visualization]'`)\n",
        "* Libreria grafica Rustworkx (`pip install rustworkx`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "03860584",
      "metadata": {},
      "source": [
        "<span id=\"set-up\" />\n",
        "\n",
        "## Impostazione di\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "88a21408",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from typing import List\n",
        "import rustworkx as rx\n",
        "import matplotlib.pyplot as plt\n",
        "from rustworkx.visualization import mpl_draw\n",
        "from qiskit.circuit import (\n",
        "    Parameter,\n",
        "    QuantumCircuit,\n",
        "    QuantumRegister,\n",
        "    ClassicalRegister,\n",
        ")\n",
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.circuit.classical import expr\n",
        "from qiskit.transpiler.preset_passmanagers import (\n",
        "    generate_preset_pass_manager,\n",
        ")\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.circuit.library import RZGate, XGate\n",
        "from qiskit.transpiler.passes import (\n",
        "    ALAPScheduleAnalysis,\n",
        "    PadDynamicalDecoupling,\n",
        ")\n",
        "\n",
        "from qiskit.transpiler.basepasses import TransformationPass\n",
        "from qiskit.circuit.measure import Measure\n",
        "from qiskit.transpiler.passes.utils.remove_final_measurements import (\n",
        "    calc_final_ops,\n",
        ")\n",
        "from qiskit.circuit import Instruction\n",
        "\n",
        "from qiskit.visualization import plot_circuit_layout\n",
        "from qiskit.circuit.tools import pi_check\n",
        "\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.primitives import SamplerV2 as Aer_Sampler\n",
        "\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    Batch,\n",
        "    SamplerV2 as Sampler,\n",
        ")\n",
        "from qiskit.providers.exceptions import QiskitBackendNotFoundError\n",
        "from qiskit_ibm_runtime.visualization import (\n",
        "    draw_circuit_schedule_timing,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "df65631b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-circuit\" />\n",
        "\n",
        "## Fase 1: mappare gli input classici su un circuito quantistico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "188f83ae",
      "metadata": {},
      "source": [
        "Iniziamo definendo il reticolo da simulare. Abbiamo scelto di lavorare con il reticolo a nido d'ape (chiamato anche esagonale), che è un grafo planare con nodi di grado 3. Qui specifichiamo la dimensione del reticolo, i parametri circuitali rilevanti di interesse nella dinamica trotterizzata. Simuliamo l'evoluzione temporale trotterizzata secondo il modello di Ising con tre diversi valori di campo magnetico locale ( $\\theta$ ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "e8bc54ef",
      "metadata": {},
      "outputs": [],
      "source": [
        "hex_rows = 3  # specify lattice size\n",
        "hex_cols = 5\n",
        "depths = range(9)  # specify Trotter steps\n",
        "zz_angle = np.pi / 8  # parameter for ZZ interaction\n",
        "max_angle = np.pi / 2  # max theta angle\n",
        "points = 3  # number of theta parameters\n",
        "\n",
        "θ = Parameter(\"θ\")\n",
        "params = np.linspace(0, max_angle, points)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "0b12f364",
      "metadata": {},
      "outputs": [],
      "source": [
        "def make_hex_lattice(hex_rows=1, hex_cols=1):\n",
        "    \"\"\"Define hexagon lattice.\"\"\"\n",
        "    hex_cmap = CouplingMap.from_hexagonal_lattice(\n",
        "        hex_rows, hex_cols, bidirectional=False\n",
        "    )\n",
        "    data = list(hex_cmap.physical_qubits)\n",
        "    graph = hex_cmap.graph.to_undirected(multigraph=False)\n",
        "    edge_colors = rx.graph_misra_gries_edge_color(graph)\n",
        "    layer_edges = {color: [] for color in edge_colors.values()}\n",
        "    for edge_index, color in edge_colors.items():\n",
        "        layer_edges[color].append(graph.edge_list()[edge_index])\n",
        "    return data, layer_edges, hex_cmap, graph"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2286ebb",
      "metadata": {},
      "source": [
        "Cominciamo con un piccolo esempio di prova:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "c011bc1a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/c011bc1a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "hex_rows_test = 1\n",
        "hex_cols_test = 2\n",
        "\n",
        "data_test, layer_edges_test, hex_cmap_test, graph_test = make_hex_lattice(\n",
        "    hex_rows=hex_rows_test, hex_cols=hex_cols_test\n",
        ")\n",
        "\n",
        "# display a small example for illustration\n",
        "node_colors_test = [\"lightblue\"] * len(graph_test.node_indices())\n",
        "pos = rx.graph_spring_layout(\n",
        "    graph_test,\n",
        "    k=5 / np.sqrt(len(graph_test.nodes())),\n",
        "    repulsive_exponent=1,\n",
        "    num_iter=150,\n",
        ")\n",
        "mpl_draw(graph_test, node_color=node_colors_test, pos=pos)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8e5f862a",
      "metadata": {},
      "source": [
        "Useremo il piccolo esempio per illustrazione e simulazione. Di seguito riportiamo anche un esempio di grandi dimensioni per mostrare che il flusso di lavoro può essere esteso a dimensioni maggiori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "ba481bd4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "num_qubits = 46\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/ba481bd4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "data, layer_edges, hex_cmap, graph = make_hex_lattice(\n",
        "    hex_rows=hex_rows, hex_cols=hex_cols\n",
        ")\n",
        "num_qubits = len(data)\n",
        "print(f\"num_qubits = {num_qubits}\")\n",
        "\n",
        "# display the honeycomb lattice to simulate\n",
        "node_colors = [\"lightblue\"] * len(graph.node_indices())\n",
        "pos = rx.graph_spring_layout(\n",
        "    graph,\n",
        "    k=5 / np.sqrt(num_qubits),\n",
        "    repulsive_exponent=1,\n",
        "    num_iter=150,\n",
        ")\n",
        "mpl_draw(graph, node_color=node_colors, pos=pos)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6eb03e83",
      "metadata": {},
      "source": [
        "<span id=\"build-unitary-circuits\" />\n",
        "\n",
        "### Costruire circuiti unitari\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f4b421fa",
      "metadata": {},
      "source": [
        "Una volta specificati la dimensione del problema e i parametri, siamo ora pronti a costruire il circuito parametrizzato che simula l'evoluzione temporale trotterizzata di $U(\\theta)$ con diversi passi di Trotter, specificati `depth` dall'argomento. Il circuito che costruiamo presenta strati alternati di porte `Rx` ( $\\theta$ ) e `Rzz` porte. I `Rzz` gate realizzano le interazioni ZZ tra gli spin accoppiati, che saranno posizionati tra ciascun sito del reticolo specificato `layer_edges` dall'argomento.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "9e2dc428",
      "metadata": {},
      "outputs": [],
      "source": [
        "def gen_hex_unitary(\n",
        "    num_qubits=6,\n",
        "    zz_angle=np.pi / 8,\n",
        "    layer_edges=[\n",
        "        [(0, 1), (2, 3), (4, 5)],\n",
        "        [(1, 2), (3, 4), (5, 0)],\n",
        "    ],\n",
        "    θ=Parameter(\"θ\"),\n",
        "    depth=1,\n",
        "    measure=False,\n",
        "    final_rot=True,\n",
        "):\n",
        "    \"\"\"Build unitary circuit.\"\"\"\n",
        "    circuit = QuantumCircuit(num_qubits)\n",
        "    # Build trotter layers\n",
        "    for _ in range(depth):\n",
        "        for i in range(num_qubits):\n",
        "            circuit.rx(θ, i)\n",
        "        circuit.barrier()\n",
        "        for coloring in layer_edges.keys():\n",
        "            for e in layer_edges[coloring]:\n",
        "                circuit.rzz(zz_angle, e[0], e[1])\n",
        "        circuit.barrier()\n",
        "    # Optional final rotation, set True to be consistent with Ref. [1]\n",
        "    if final_rot:\n",
        "        for i in range(num_qubits):\n",
        "            circuit.rx(θ, i)\n",
        "    if measure:\n",
        "        circuit.measure_all()\n",
        "\n",
        "    return circuit"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "24235b4a",
      "metadata": {},
      "source": [
        "Visualizza il piccolo circuito di prova:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "268e6999",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/268e6999-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circ_unitary_test = gen_hex_unitary(\n",
        "    num_qubits=len(data_test),\n",
        "    layer_edges=layer_edges_test,\n",
        "    θ=Parameter(\"θ\"),\n",
        "    depth=1,\n",
        "    measure=True,\n",
        ")\n",
        "circ_unitary_test.draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a8abb0d",
      "metadata": {},
      "source": [
        "Allo stesso modo, costruisci i circuiti unitari dell'esempio grande a diversi passi di Trotter e l'osservabile per stimare il valore atteso.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6c9e388a",
      "metadata": {},
      "outputs": [],
      "source": [
        "circuits_unitary = []\n",
        "for depth in depths:\n",
        "    circ = gen_hex_unitary(\n",
        "        num_qubits=num_qubits,\n",
        "        layer_edges=layer_edges,\n",
        "        θ=Parameter(\"θ\"),\n",
        "        depth=depth,\n",
        "        measure=True,\n",
        "    )\n",
        "    circuits_unitary.append(circ)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "695e2bad",
      "metadata": {},
      "outputs": [],
      "source": [
        "observables_unitary = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [i], 1 / num_qubits) for i in range(num_qubits)],\n",
        "    num_qubits=num_qubits,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "84d2cd91",
      "metadata": {},
      "source": [
        "<span id=\"build-dynamic-circuit-implementation\" />\n",
        "\n",
        "### Costruire un'implementazione dinamica del circuito\n",
        "\n",
        "Questa sezione illustra l'implementazione del circuito dinamico principale per simulare la stessa evoluzione temporale trotterizzata. Si noti che il reticolo a nido d'ape che vogliamo simulare non corrisponde al reticolo pesante dei qubit hardware. Un modo semplice per mappare il circuito sull'hardware consiste nell'introdurre una serie di operazioni SWAP per avvicinare i qubit interagenti tra loro, in modo da realizzare l'interazione ZZ. Qui mettiamo in evidenza un approccio alternativo che utilizza circuiti dinamici come soluzione, il quale dimostra che è possibile utilizzare la combinazione di calcolo quantistico e calcolo classico in tempo reale all'interno di un circuito in Qiskit per realizzare interazioni oltre il vicino più prossimo.\n",
        "\n",
        "Nell'implementazione del circuito dinamico, l'interazione ZZ viene efficacemente implementata utilizzando qubit ancilla, misurazione a metà circuito e feedforward. Per comprendere questo concetto, occorre tenere presente che le rotazioni ZZ applicano un fattore di fase $e^{i\\theta}$ allo stato in base alla sua parità. Per due qubit, gli stati di base computazionali sono $|00\\rangle$, $|01\\rangle$, $|10\\rangle$ e $|11\\rangle$. Il gate di rotazione ZZ applica un fattore di fase agli stati $|01\\rangle$ e $|10\\rangle$ la cui parità (il numero di uno nello stato) è dispari e lascia invariati gli stati di parità pari. Di seguito viene descritto come implementare efficacemente le interazioni ZZ su due qubit utilizzando circuiti dinamici.\n",
        "\n",
        "1. Calcolare la parità in un qubit ancilla: invece di applicare direttamente ZZ a due qubit, introduciamo un terzo qubit, il qubit ancilla, per memorizzare le informazioni di parità dei due qubit di dati. Intrecciamo l'ancilla con ciascun qubit di dati utilizzando porte CX dal qubit di dati al qubit ancilla.\n",
        "\n",
        "2. Applicare una rotazione Z a un singolo qubit all'ancilla qubit: questo perché l'ancilla possiede le informazioni di parità dei due qubit di dati, il che implementa efficacemente la rotazione ZZ sui qubit di dati.\n",
        "\n",
        "3. Misura il qubit ancilla nella base X: questo è il passaggio chiave che fa crollare lo stato del qubit ancilla, e il risultato della misurazione ci dice cosa è successo:\n",
        "\n",
        "   * Misura 0: quando si osserva un risultato pari a 0, significa che abbiamo applicato correttamente una rotazione dell' $ZZ(\\theta)$ e ai nostri qubit di dati.\n",
        "\n",
        "   * Misura 1: quando si osserva un risultato pari a 1, abbiamo applicato invece l' $ZZ(\\theta + \\pi)$.\n",
        "\n",
        "4. Applicare il gate di correzione durante la misurazione 1: se abbiamo misurato 1, applichiamo i gate Z ai qubit di dati per \"correggere\" la fase extra $\\pi$.\n",
        "\n",
        "Il circuito risultante è il seguente:\n",
        "\n",
        "![implementazione dinamica](https://quantum.cloud.ibm.com/docs/images/tutorials/dc-hex-ising/circuit-1.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9872fed2",
      "metadata": {},
      "source": [
        "Quando adottiamo questo approccio per simulare un reticolo a nido d'ape, il circuito risultante si integra perfettamente nell'hardware con un reticolo esagonale pesante: tutti i qubit di dati risiedono sui siti degree-3 del reticolo, che forma un reticolo esagonale. Ogni coppia di qubit di dati condivide un qubit ancillare che risiede in un sito dell' degree-2. Di seguito, costruiamo il reticolo di qubit per l'implementazione del circuito dinamico, introducendo i qubit ancilla (mostrati nei cerchi viola più scuri).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "c0e7afd2",
      "metadata": {},
      "outputs": [],
      "source": [
        "def make_lattice(hex_rows=1, hex_cols=1):\n",
        "    \"\"\"Define heavy-hex lattice and corresponding lists of data and ancilla nodes.\"\"\"\n",
        "    hex_cmap = CouplingMap.from_hexagonal_lattice(\n",
        "        hex_rows, hex_cols, bidirectional=False\n",
        "    )\n",
        "    data = list(hex_cmap.physical_qubits)\n",
        "\n",
        "    heavyhex_cmap = CouplingMap()\n",
        "    for d in data:\n",
        "        heavyhex_cmap.add_physical_qubit(d)\n",
        "\n",
        "    # make coupling map\n",
        "    a = len(data)\n",
        "    for edge in hex_cmap.get_edges():\n",
        "        heavyhex_cmap.add_physical_qubit(a)\n",
        "        heavyhex_cmap.add_edge(edge[0], a)\n",
        "        heavyhex_cmap.add_edge(edge[1], a)\n",
        "        a += 1\n",
        "    ancilla = list(range(len(data), a))\n",
        "    qubits = data + ancilla\n",
        "\n",
        "    # color edges\n",
        "    graph = heavyhex_cmap.graph.to_undirected(multigraph=False)\n",
        "    edge_colors = rx.graph_misra_gries_edge_color(graph)\n",
        "    layer_edges = {color: [] for color in edge_colors.values()}\n",
        "    for edge_index, color in edge_colors.items():\n",
        "        layer_edges[color].append(graph.edge_list()[edge_index])\n",
        "\n",
        "    # construct observable\n",
        "    obs_hex = SparsePauliOp.from_sparse_list(\n",
        "        [(\"Z\", [i], 1 / len(data)) for i in data],\n",
        "        num_qubits=len(qubits),\n",
        "    )\n",
        "\n",
        "    return (data, qubits, ancilla, layer_edges, heavyhex_cmap, graph, obs_hex)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5c39eeab",
      "metadata": {},
      "source": [
        "Visualizza il reticolo esagonale pesante per i qubit di dati e i qubit ancilla su piccola scala:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "2d7224ef",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "number of data qubits = 46\n",
            "number of ancilla qubits = 60\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/2d7224ef-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "(data, qubits, ancilla, layer_edges, heavyhex_cmap, graph, obs_hex) = (\n",
        "    make_lattice(hex_rows=hex_rows, hex_cols=hex_cols)\n",
        ")\n",
        "\n",
        "print(f\"number of data qubits = {len(data)}\")\n",
        "print(f\"number of ancilla qubits = {len(ancilla)}\")\n",
        "\n",
        "node_colors = []\n",
        "for node in graph.node_indices():\n",
        "    if node in ancilla:\n",
        "        node_colors.append(\"purple\")\n",
        "    else:\n",
        "        node_colors.append(\"lightblue\")\n",
        "\n",
        "pos = rx.graph_spring_layout(\n",
        "    graph,\n",
        "    k=1 / np.sqrt(len(qubits)),\n",
        "    repulsive_exponent=2,\n",
        "    num_iter=200,\n",
        ")\n",
        "\n",
        "# Visualize the graph, blue circles are data qubits and purple circles are ancillas\n",
        "mpl_draw(graph, node_color=node_colors, pos=pos)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf9177d2",
      "metadata": {},
      "source": [
        "Di seguito, costruiamo il circuito dinamico per l'evoluzione temporale trotterizzata. I `RZZ` gate vengono sostituiti con l'implementazione del circuito dinamico utilizzando i passaggi descritti sopra.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "4c98664f",
      "metadata": {},
      "outputs": [],
      "source": [
        "def gen_hex_dynamic(\n",
        "    depth=1,\n",
        "    zz_angle=np.pi / 8,\n",
        "    θ=Parameter(\"θ\"),\n",
        "    hex_rows=1,\n",
        "    hex_cols=1,\n",
        "    measure=False,\n",
        "    add_dd=True,\n",
        "):\n",
        "    \"\"\"Build dynamic circuits.\"\"\"\n",
        "    (data, qubits, ancilla, layer_edges, heavyhex_cmap, graph, obs_hex) = (\n",
        "        make_lattice(hex_rows=hex_rows, hex_cols=hex_cols)\n",
        "    )\n",
        "    # Initialize circuit\n",
        "    qr = QuantumRegister(len(qubits), \"qr\")\n",
        "    cr = ClassicalRegister(len(ancilla), \"cr\")\n",
        "    circuit = QuantumCircuit(qr, cr)\n",
        "\n",
        "    for k in range(depth):\n",
        "        # Single-qubit Rx layer\n",
        "        for d in data:\n",
        "            circuit.rx(θ, d)\n",
        "        circuit.barrier()\n",
        "\n",
        "        # CX gates from data qubits to ancilla qubits\n",
        "        for same_color_edges in layer_edges.values():\n",
        "            for e in same_color_edges:\n",
        "                circuit.cx(e[0], e[1])\n",
        "        circuit.barrier()\n",
        "\n",
        "        # Apply Rz rotation on ancilla qubits and rotate into X basis\n",
        "        for a in ancilla:\n",
        "            circuit.rz(zz_angle, a)\n",
        "            circuit.h(a)\n",
        "        # Add barrier to align terminal measurement\n",
        "        circuit.barrier()\n",
        "\n",
        "        # Measure ancilla qubits\n",
        "        for i, a in enumerate(ancilla):\n",
        "            circuit.measure(a, i)\n",
        "        d2ros = {}\n",
        "        a2ro = {}\n",
        "        # Retrieve ancilla measurement outcomes\n",
        "        for a in ancilla:\n",
        "            a2ro[a] = cr[ancilla.index(a)]\n",
        "\n",
        "        # For each data qubit, retrieve measurement outcomes of neighboring\n",
        "        # ancilla qubits\n",
        "        for d in data:\n",
        "            ros = [a2ro[a] for a in heavyhex_cmap.neighbors(d)]\n",
        "            d2ros[d] = ros\n",
        "\n",
        "        # Build classical feedforward operations (optionally add DD on idling\n",
        "        # data qubits)\n",
        "        for d in data:\n",
        "            if add_dd:\n",
        "                circuit = add_stretch_dd(circuit, d, f\"data_{d}_depth_{k}\")\n",
        "\n",
        "            # # XOR the neighboring readouts of the data qubit;\n",
        "            # if True, apply Z to it\n",
        "            ros = d2ros[d]\n",
        "            parity = ros[0]\n",
        "            for ro in ros[1:]:\n",
        "                parity = expr.bit_xor(parity, ro)\n",
        "            with circuit.if_test(expr.equal(parity, True)):\n",
        "                circuit.z(d)\n",
        "\n",
        "        # Reset the ancilla if its readout is 1\n",
        "        for a in ancilla:\n",
        "            with circuit.if_test(expr.equal(a2ro[a], True)):\n",
        "                circuit.x(a)\n",
        "        circuit.barrier()\n",
        "\n",
        "    # Final single-qubit Rx layer to match the unitary circuits\n",
        "    for d in data:\n",
        "        circuit.rx(θ, d)\n",
        "\n",
        "    if measure:\n",
        "        circuit.measure_all()\n",
        "    return circuit, obs_hex\n",
        "\n",
        "\n",
        "def add_stretch_dd(qc, q, name):\n",
        "    \"\"\"Add XpXm DD sequence.\"\"\"\n",
        "    s = qc.add_stretch(name)\n",
        "    qc.delay(s, q)\n",
        "    qc.x(q)\n",
        "    qc.delay(s, q)\n",
        "    qc.delay(s, q)\n",
        "    qc.rz(np.pi, q)\n",
        "    qc.x(q)\n",
        "    qc.rz(-np.pi, q)\n",
        "    qc.delay(s, q)\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c8b3b632",
      "metadata": {},
      "source": [
        "<span id=\"dynamical-decoupling-dd-and-support-for-stretch-duration\" />\n",
        "\n",
        "#### Disaccoppiamento dinamico (DD) e supporto per `stretch` la durata\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f08bda1",
      "metadata": {},
      "source": [
        "Un avvertimento sull'uso dell'implementazione del circuito dinamico per realizzare l'interazione ZZ è che la misurazione a metà circuito e le operazioni classiche di feedforward richiedono in genere più tempo per essere eseguite rispetto ai gate quantistici. Per sopprimere la decoerenza dei qubit durante il tempo di inattività necessario per l'esecuzione delle operazioni classiche, abbiamo aggiunto una sequenza [di disaccoppiamento dinamico](/docs/guides/error-mitigation-and-suppression-techniques#dynamical-decoupling) (DD) dopo l'operazione di misurazione sui qubit ancilla e prima dell'operazione Z condizionale sul qubit dati, prima dell'istruzione `if_test` .\n",
        "\n",
        "La sequenza DD viene generata dalla funzione `add_stretch_dd()`, che utilizza le [durate `stretch`](/docs/guides/stretch) per determinare gli intervalli di tempo tra i gate DD. Una durata `stretch` è un modo per specificare un intervallo di tempo estensibile per l'operazione `delay` , in modo tale che la durata del ritardo possa aumentare fino a coprire il tempo di inattività del qubit. Le variabili di durata specificate da `stretch` vengono risolte in fase di compilazione nelle durate desiderate che soddisfano un determinato vincolo. Ciò risulta molto utile quando la temporizzazione delle sequenze DD è fondamentale per ottenere buone prestazioni in termini di soppressione degli errori. Per ulteriori dettagli su questo tipo `stretch` , consultare la documentazione di [OpenQASM](https://openqasm.com/language/delays.html#duration-and-stretch-types). Al momento, il supporto per questo tipo `stretch` è in fase sperimentale. Per ulteriori dettagli sui vincoli di utilizzo, si prega di consultare [la sezione](/docs/guides/stretch#qiskit-runtime-limitations) dedicata alle limitazioni nella documentazione `stretch` .\n",
        "\n",
        "Utilizzando le funzioni definite sopra, costruiamo i circuiti di evoluzione temporale trotterizzati, con e senza DD, e le corrispondenti osservabili.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "997419c8",
      "metadata": {},
      "source": [
        "Iniziamo visualizzando il circuito dinamico di un piccolo esempio:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "b6e2e76c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/b6e2e76c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "hex_rows_test = 1\n",
        "hex_cols_test = 1\n",
        "\n",
        "(\n",
        "    data_test,\n",
        "    qubits_test,\n",
        "    ancilla_test,\n",
        "    layer_edges_test,\n",
        "    heavyhex_cmap_test,\n",
        "    graph_test,\n",
        "    obs_hex_test,\n",
        ") = make_lattice(hex_rows=hex_rows_test, hex_cols=hex_cols_test)\n",
        "\n",
        "node_colors = []\n",
        "for node in graph_test.node_indices():\n",
        "    if node in ancilla_test:\n",
        "        node_colors.append(\"purple\")\n",
        "    else:\n",
        "        node_colors.append(\"lightblue\")\n",
        "pos = rx.graph_spring_layout(\n",
        "    graph_test,\n",
        "    k=5 / np.sqrt(len(qubits_test)),\n",
        "    repulsive_exponent=2,\n",
        "    num_iter=150,\n",
        ")\n",
        "\n",
        "# display a small example for illustration\n",
        "node_colors_test = [\"lightblue\"] * len(graph_test.node_indices())\n",
        "mpl_draw(graph_test, node_color=node_colors, pos=pos)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "735e590a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/735e590a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit_dynamic_test, obs_dynamic_test = gen_hex_dynamic(\n",
        "    depth=1,\n",
        "    θ=Parameter(\"θ\"),\n",
        "    hex_rows=hex_rows_test,\n",
        "    hex_cols=hex_cols_test,\n",
        "    measure=False,\n",
        "    add_dd=False,\n",
        ")\n",
        "circuit_dynamic_test.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5de9381a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/5de9381a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit_dynamic_dd_test, _ = gen_hex_dynamic(\n",
        "    depth=1,\n",
        "    θ=Parameter(\"θ\"),\n",
        "    hex_rows=hex_rows_test,\n",
        "    hex_cols=hex_cols_test,\n",
        "    measure=False,\n",
        "    add_dd=True,\n",
        ")\n",
        "circuit_dynamic_dd_test.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "956dd43e",
      "metadata": {},
      "source": [
        "Allo stesso modo, costruisci i circuiti dinamici per l'esempio più grande:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "bd7b2be0",
      "metadata": {},
      "outputs": [],
      "source": [
        "circuits_dynamic = []\n",
        "circuits_dynamic_dd = []\n",
        "observables_dynamic = []\n",
        "for depth in depths:\n",
        "    circuit, obs = gen_hex_dynamic(\n",
        "        depth=depth,\n",
        "        θ=Parameter(\"θ\"),\n",
        "        hex_rows=hex_rows,\n",
        "        hex_cols=hex_cols,\n",
        "        measure=True,\n",
        "        add_dd=False,\n",
        "    )\n",
        "    circuits_dynamic.append(circuit)\n",
        "\n",
        "    circuit_dd, _ = gen_hex_dynamic(\n",
        "        depth=depth,\n",
        "        θ=Parameter(\"θ\"),\n",
        "        hex_rows=hex_rows,\n",
        "        hex_cols=hex_cols,\n",
        "        measure=True,\n",
        "        add_dd=True,\n",
        "    )\n",
        "    circuits_dynamic_dd.append(circuit_dd)\n",
        "    observables_dynamic.append(obs)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f0fdf70",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-hardware-execution\" />\n",
        "\n",
        "## Fase 2: Ottimizzazione del problema per l'esecuzione hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "86642d2b",
      "metadata": {},
      "source": [
        "Ora siamo pronti per trasporre il circuito sull'hardware. Trasformeremo sia l'implementazione dello standard unitario che l'implementazione del circuito dinamico nell'hardware.\n",
        "\n",
        "Per eseguire la transpilazione sull'hardware, prima istanziamo il backend. Se disponibile, sceglieremo un backend che supporti l'istruzione [`MidCircuitMeasure`](/docs/guides/measure-qubits) (`measure_2`).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "ec904b18",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "try:\n",
        "    backend = service.least_busy(\n",
        "        operational=True,\n",
        "        simulator=False,\n",
        "        use_fractional_gates=True,\n",
        "        filters=lambda b: \"measure_2\" in b.supported_instructions,\n",
        "    )\n",
        "except QiskitBackendNotFoundError:\n",
        "    backend = service.least_busy(\n",
        "        operational=True,\n",
        "        simulator=False,\n",
        "        use_fractional_gates=True,\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "610622b4",
      "metadata": {},
      "source": [
        "<span id=\"transpilation-for-dynamic-circuits\" />\n",
        "\n",
        "### Transpilazione per circuiti dinamici\n",
        "\n",
        "Per prima cosa, transpiliamo i circuiti dinamici, con e senza l'aggiunta della sequenza DD. Per garantire l'utilizzo dello stesso set di qubit fisici in tutti i circuiti e ottenere risultati più coerenti, prima transpiliamo il circuito una volta, quindi utilizziamo il suo layout per tutti i circuiti successivi, specificati da [`initial_layout`](/docs/api/qiskit/qiskit.transpiler.TranspileLayout#initial_layout) nel pass manager. Quindi costruiamo i [blocchi primitivi unificati](/docs/guides/primitive-input-output) (PUB) come input primitivo del campionatore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "aa653907",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_temp = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "isa_temp = pm_temp.run(circuits_dynamic[-1])\n",
        "dynamic_layout = isa_temp.layout.initial_index_layout(filter_ancillas=True)\n",
        "\n",
        "pm = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, initial_layout=dynamic_layout\n",
        ")\n",
        "\n",
        "dynamic_isa_circuits = [pm.run(circ) for circ in circuits_dynamic]\n",
        "dynamic_pubs = [(circ, params) for circ in dynamic_isa_circuits]\n",
        "\n",
        "dynamic_isa_circuits_dd = [pm.run(circ) for circ in circuits_dynamic_dd]\n",
        "dynamic_pubs_dd = [(circ, params) for circ in dynamic_isa_circuits_dd]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6979ad68",
      "metadata": {},
      "source": [
        "Possiamo visualizzare la disposizione dei qubit del circuito transpilato qui sotto. I cerchi neri indicano i qubit di dati e i qubit ancilla utilizzati nell'implementazione del circuito dinamico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "65b2df70",
      "metadata": {},
      "outputs": [],
      "source": [
        "def _heron_coords_r2():\n",
        "    cord_map = np.array(\n",
        "        [\n",
        "            [\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                3,\n",
        "                7,\n",
        "                11,\n",
        "                15,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                1,\n",
        "                5,\n",
        "                9,\n",
        "                13,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                3,\n",
        "                7,\n",
        "                11,\n",
        "                15,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                1,\n",
        "                5,\n",
        "                9,\n",
        "                13,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                3,\n",
        "                7,\n",
        "                11,\n",
        "                15,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                1,\n",
        "                5,\n",
        "                9,\n",
        "                13,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "                3,\n",
        "                7,\n",
        "                11,\n",
        "                15,\n",
        "                0,\n",
        "                1,\n",
        "                2,\n",
        "                3,\n",
        "                4,\n",
        "                5,\n",
        "                6,\n",
        "                7,\n",
        "                8,\n",
        "                9,\n",
        "                10,\n",
        "                11,\n",
        "                12,\n",
        "                13,\n",
        "                14,\n",
        "                15,\n",
        "            ],\n",
        "            -1\n",
        "            * np.array([j for i in range(15) for j in [i] * [16, 4][i % 2]]),\n",
        "        ],\n",
        "        dtype=int,\n",
        "    )\n",
        "\n",
        "    hcords = []\n",
        "    ycords = cord_map[0]\n",
        "    xcords = cord_map[1]\n",
        "    for i in range(156):\n",
        "        hcords.append([xcords[i] + 1, np.abs(ycords[i]) + 1])\n",
        "\n",
        "    return hcords"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "98d402e0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/98d402e0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_circuit_layout(\n",
        "    dynamic_isa_circuits_dd[8],\n",
        "    backend,\n",
        "    qubit_coordinates=_heron_coords_r2(),\n",
        "    view=\"virtual\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1bff4d50",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  Se ricevi errori relativi a `neato` non trovato da `plot_circuit_layout()`, assicurati di avere il `graphviz` pacchetto installato e disponibile nel tuo PATH. Se viene installato in una posizione non predefinita (ad esempio, utilizzando `homebrew` su MacOS ), potrebbe essere necessario aggiornare la variabile `PATH` di ambiente. Questo può essere fatto all'interno di questo notebook utilizzando quanto segue:\n",
        "\n",
        "  ```python\n",
        "  import os\n",
        "  os.environ['PATH'] = f\"path/to/neato{os.pathsep}{os.environ['PATH']}\"\n",
        "  ```\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "82fb6fa8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/82fb6fa8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "dynamic_isa_circuits[1].draw(fold=-1, output=\"mpl\", idle_wires=False)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "99ad295c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/99ad295c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "dynamic_isa_circuits_dd[1].draw(fold=-1, output=\"mpl\", idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "47d43b9e",
      "metadata": {},
      "source": [
        "<span id=\"transpile-using-midcircuitmeasure\" />\n",
        "\n",
        "#### Transpile utilizzando `MidCircuitMeasure`\n",
        "\n",
        "`MidCircuitMeasure` è un'aggiunta alle operazioni di misurazione disponibili, calibrata specificamente per eseguire [misurazioni a metà circuito](/docs/guides/execute-dynamic-circuits#midcircuit). L'istruzione `MidCircuitMeasure` corrisponde `measure_2` all'istruzione supportata dai backend. Si noti che non `measure_2` è supportato su tutti i backend. Puoi usare `service.backends(filters=lambda b: \"measure_2\" in b.supported_instructions)` per trovare i backend che lo supportano. Qui mostriamo come traspilare il circuito in modo che le misurazioni a metà circuito definite nel circuito vengano eseguite utilizzando `MidCircuitMeasure` l'operazione, se il backend la supporta.\n",
        "\n",
        "Di seguito riportiamo la durata `measure_2` dell'istruzione e dell'istruzione `measure` standard.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "de870864",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mid-circuit measurement `measure_2` duration: 1.3800000000000003 μs\n",
            "Terminal measurement `measure` duration: 2.1800000000000006 μs\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f'Mid-circuit measurement `measure_2` duration: '\n",
        "    f'{backend.instruction_durations.get('measure_2',0) * backend.dt * 1e9/1e3} μs'\n",
        ")\n",
        "print(\n",
        "    f'Terminal measurement `measure` duration: '\n",
        "    f'{backend.instruction_durations.get('measure',0) * backend.dt *1e9/1e3} μs'\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "a1bdc9e7",
      "metadata": {},
      "outputs": [],
      "source": [
        "\"\"\"Pass that replaces terminal measures in the middle of the circuit with\n",
        "MidCircuitMeasure instructions.\"\"\"\n",
        "\n",
        "\n",
        "class ConvertToMidCircuitMeasure(TransformationPass):\n",
        "    \"\"\"This pass replaces terminal measures in the middle of the circuit with\n",
        "    MidCircuitMeasure instructions.\n",
        "    \"\"\"\n",
        "\n",
        "    def __init__(self, target):\n",
        "        super().__init__()\n",
        "        self.target = target\n",
        "\n",
        "    def run(self, dag):\n",
        "        \"\"\"Run the pass on a dag.\"\"\"\n",
        "        mid_circ_measure = None\n",
        "        for inst in self.target.instructions:\n",
        "            if isinstance(inst[0], Instruction) and inst[0].name.startswith(\n",
        "                \"measure_\"\n",
        "            ):\n",
        "                mid_circ_measure = inst[0]\n",
        "                break\n",
        "        if not mid_circ_measure:\n",
        "            return dag\n",
        "\n",
        "        final_measure_nodes = calc_final_ops(dag, {\"measure\"})\n",
        "        for node in dag.op_nodes(Measure):\n",
        "            if node not in final_measure_nodes:\n",
        "                dag.substitute_node(node, mid_circ_measure, inplace=True)\n",
        "\n",
        "        return dag\n",
        "\n",
        "\n",
        "pm = PassManager(ConvertToMidCircuitMeasure(backend.target))\n",
        "\n",
        "dynamic_isa_circuits_meas2 = [pm.run(circ) for circ in dynamic_isa_circuits]\n",
        "dynamic_pubs_meas2 = [(circ, params) for circ in dynamic_isa_circuits_meas2]\n",
        "\n",
        "dynamic_isa_circuits_dd_meas2 = [\n",
        "    pm.run(circ) for circ in dynamic_isa_circuits_dd\n",
        "]\n",
        "dynamic_pubs_dd_meas2 = [\n",
        "    (circ, params) for circ in dynamic_isa_circuits_dd_meas2\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1e982ee",
      "metadata": {},
      "source": [
        "<span id=\"transpilation-for-unitary-circuits\" />\n",
        "\n",
        "### Traspilazione per circuiti unitari\n",
        "\n",
        "Per stabilire un confronto equo tra i circuiti dinamici e la loro controparte unitaria, utilizziamo lo stesso insieme di qubit fisici utilizzati nei circuiti dinamici per i qubit di dati come layout per la transpilazione dei circuiti unitari.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "87962c09",
      "metadata": {},
      "outputs": [],
      "source": [
        "init_layout = [\n",
        "    dynamic_layout[ind] for ind in range(circuits_unitary[0].num_qubits)\n",
        "]\n",
        "\n",
        "\n",
        "pm = generate_preset_pass_manager(\n",
        "    target=backend.target,\n",
        "    initial_layout=init_layout,\n",
        "    optimization_level=3,\n",
        ")\n",
        "\n",
        "\n",
        "def transpile_minimize(circ: QuantumCircuit, pm: PassManager, iterations=10):\n",
        "    \"\"\"Transpile circuits for specified number of iterations and return the one\n",
        "    with smallest two-qubit gate depth\"\"\"\n",
        "    circs = [pm.run(circ) for i in range(iterations)]\n",
        "    circs_sorted = sorted(\n",
        "        circs,\n",
        "        key=lambda x: x.depth(lambda x: x.operation.num_qubits == 2),\n",
        "    )\n",
        "    return circs_sorted[0]\n",
        "\n",
        "\n",
        "unitary_isa_circuits = []\n",
        "for circ in circuits_unitary:\n",
        "    circ_t = transpile_minimize(circ, pm, iterations=100)\n",
        "    unitary_isa_circuits.append(circ_t)\n",
        "\n",
        "unitary_pubs = [(circ, params) for circ in unitary_isa_circuits]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e7da0161",
      "metadata": {},
      "source": [
        "Visualizziamo la disposizione dei qubit dei circuiti unitari transpilati. I cerchi neri indicano i qubit fisici utilizzati per trasporre i circuiti unitari e i loro indici corrispondono agli indici dei qubit virtuali. Confrontando questo dato con il layout tracciato per i circuiti dinamici, possiamo confermare che i circuiti unitari utilizzano lo stesso insieme di qubit fisici dei qubit di dati nei circuiti dinamici.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "8c3c633f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/8c3c633f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_circuit_layout(\n",
        "    unitary_isa_circuits[-1],\n",
        "    backend,\n",
        "    qubit_coordinates=_heron_coords_r2(),\n",
        "    view=\"virtual\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2fd70904",
      "metadata": {},
      "source": [
        "Ora aggiungiamo la sequenza DD ai circuiti transpilati e costruiamo i PUB corrispondenti per l'invio dei lavori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "383ba663",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_dd = PassManager(\n",
        "    [\n",
        "        ALAPScheduleAnalysis(target=backend.target),\n",
        "        PadDynamicalDecoupling(\n",
        "            dd_sequence=[\n",
        "                XGate(),\n",
        "                RZGate(np.pi),\n",
        "                XGate(),\n",
        "                RZGate(-np.pi),\n",
        "            ],\n",
        "            spacing=[1 / 4, 1 / 2, 0, 0, 1 / 4],\n",
        "            target=backend.target,\n",
        "        ),\n",
        "    ]\n",
        ")\n",
        "\n",
        "unitary_isa_circuits_dd = pm_dd.run(unitary_isa_circuits)\n",
        "unitary_pubs_dd = [(circ, params) for circ in unitary_isa_circuits_dd]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4980277",
      "metadata": {},
      "source": [
        "<span id=\"compare-two-qubit-gate-depth-of-unitary-and-dynamic-circuits\" />\n",
        "\n",
        "### Confronta la profondità del gate a due qubit dei circuiti unitari e dinamici\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "36f1d72d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x12628b0e0>"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/36f1d72d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# compare circuit depth of unitary and dynamic circuit implementations\n",
        "unitary_depth = [\n",
        "    unitary_isa_circuits[i].depth(lambda x: x.operation.num_qubits == 2)\n",
        "    for i in range(len(unitary_isa_circuits))\n",
        "]\n",
        "\n",
        "dynamic_depth = [\n",
        "    dynamic_isa_circuits[i].depth(lambda x: x.operation.num_qubits == 2)\n",
        "    for i in range(len(dynamic_isa_circuits))\n",
        "]\n",
        "\n",
        "plt.plot(\n",
        "    list(range(len(unitary_depth))),\n",
        "    unitary_depth,\n",
        "    label=\"unitary circuits\",\n",
        "    color=\"#be95ff\",\n",
        ")\n",
        "plt.plot(\n",
        "    list(range(len(dynamic_depth))),\n",
        "    dynamic_depth,\n",
        "    label=\"dynamic circuits\",\n",
        "    color=\"#ff7eb6\",\n",
        ")\n",
        "plt.xlabel(\"Trotter steps\")\n",
        "plt.ylabel(\"Two-qubit depth\")\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c566243b",
      "metadata": {},
      "source": [
        "Il vantaggio principale del circuito basato sulla misurazione è che, quando si implementano più interazioni ZZ, gli strati CX possono essere parallelizzati e le misurazioni possono avvenire simultaneamente. Questo perché tutte le interazioni ZZ sono commutative, quindi il calcolo può essere eseguito con profondità di misurazione 1. Dopo aver trasposto i circuiti, osserviamo che l'approccio dinamico produce una profondità di due qubit significativamente più breve rispetto all'approccio unitario standard, con l'avvertenza che la misurazione aggiuntiva a metà circuito e il feedforward classico richiedono tempo e introducono fonti di errore proprie.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "206a7278",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "53a3e876",
      "metadata": {},
      "source": [
        "<span id=\"local-testing-mode\" />\n",
        "\n",
        "#### Modalità di test locale\n",
        "\n",
        "Prima di inviare i lavori all'hardware, possiamo eseguire una piccola simulazione di prova del circuito dinamico utilizzando la [modalità di test locale](/docs/guides/local-testing-mode).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "cdfd9576",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Simulated average magnetization at trotter step = 1 at three theta values\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "array([ 0.16666667,  0.01529948, -0.14290365])"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "aer_sim = AerSimulator()\n",
        "pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)\n",
        "circuit_dynamic_test.measure_all()\n",
        "isa_qc = pm.run(circuit_dynamic_test)\n",
        "with Batch(backend=aer_sim) as batch:\n",
        "    sampler = Sampler(mode=batch)\n",
        "    result = sampler.run([(isa_qc, params)]).result()\n",
        "\n",
        "print(\n",
        "    \"Simulated average magnetization at trotter step = 1 at three theta values\"\n",
        ")\n",
        "result[0].data[\"meas\"].expectation_values(obs_dynamic_test[0])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59a3a8a0",
      "metadata": {},
      "source": [
        "<span id=\"mps-simulation\" />\n",
        "\n",
        "#### Simulazione MPS\n",
        "\n",
        "Per circuiti di grandi dimensioni, possiamo utilizzare il simulatore `matrix_product_state` (MPS), che fornisce un risultato approssimativo al valore atteso in base alla dimensione del legame scelta. Successivamente utilizziamo i risultati della simulazione MPS come base di riferimento per confrontare i risultati ottenuti dall'hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "6fab4462",
      "metadata": {},
      "outputs": [],
      "source": [
        "# The MPS simulation below took approximately 7 minutes to run on a\n",
        "# laptop with Apple M1 chip\n",
        "\n",
        "mps_backend = AerSimulator(\n",
        "    method=\"matrix_product_state\",\n",
        "    matrix_product_state_truncation_threshold=1e-5,\n",
        "    matrix_product_state_max_bond_dimension=100,\n",
        ")\n",
        "mps_sampler = Aer_Sampler.from_backend(mps_backend)\n",
        "\n",
        "shots = 4096\n",
        "\n",
        "data_sim = []\n",
        "for j in range(points):\n",
        "    circ_list = [\n",
        "        circ.assign_parameters([params[j]]) for circ in circuits_unitary\n",
        "    ]\n",
        "\n",
        "    mps_job = mps_sampler.run(circ_list, shots=shots)\n",
        "    result = mps_job.result()\n",
        "\n",
        "    point_data = [\n",
        "        result[d].data[\"meas\"].expectation_values(observables_unitary)\n",
        "        for d in depths\n",
        "    ]\n",
        "\n",
        "    data_sim.append(point_data)  # data at one theta value\n",
        "\n",
        "data_sim = np.array(data_sim)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ebd28d97",
      "metadata": {},
      "source": [
        "Una volta preparati i circuiti e gli osservabili, li eseguiamo sull'hardware utilizzando la primitiva Sampler.\n",
        "\n",
        "Qui inviamo tre lavori per `unitary_pubs`, `dynamic_pubs`, e `dynamic_pubs_dd`. Ciascuno è un elenco di circuiti parametrizzati corrispondenti a nove diversi passaggi di Trotter con tre diversi parametri dell' $\\theta$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "76b5e07e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "unitary: d96s4b52su3c739hakrg\n",
            "unitary_dd: d96s4bt2su3c739haksg\n",
            "dynamic: d96s4c0tcv6s73dk55mg\n",
            "dynamic_dd: d96s4ckqp3as739qvid0\n",
            "dynamic_meas2: d96s4csqp3as739qvie0\n",
            "dynamic_dd_meas2: d96s4daf47jc73a5v8s0\n"
          ]
        }
      ],
      "source": [
        "shots = 10000\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    sampler = Sampler(mode=batch)\n",
        "\n",
        "    sampler.options.experimental = {\n",
        "        \"execution\": {\n",
        "            \"scheduler_timing\": True\n",
        "        },  # set to True to retrieve circuit timing info\n",
        "    }\n",
        "\n",
        "    job_unitary = sampler.run(unitary_pubs, shots=shots)\n",
        "    print(f\"unitary: {job_unitary.job_id()}\")\n",
        "\n",
        "    job_unitary_dd = sampler.run(unitary_pubs_dd, shots=shots)\n",
        "    print(f\"unitary_dd: {job_unitary_dd.job_id()}\")\n",
        "\n",
        "    job_dynamic = sampler.run(dynamic_pubs, shots=shots)\n",
        "    print(f\"dynamic: {job_dynamic.job_id()}\")\n",
        "\n",
        "    job_dynamic_dd = sampler.run(dynamic_pubs_dd, shots=shots)\n",
        "    print(f\"dynamic_dd: {job_dynamic_dd.job_id()}\")\n",
        "\n",
        "    job_dynamic_meas2 = sampler.run(dynamic_pubs_meas2, shots=shots)\n",
        "    print(f\"dynamic_meas2: {job_dynamic_meas2.job_id()}\")\n",
        "\n",
        "    job_dynamic_dd_meas2 = sampler.run(dynamic_pubs_dd_meas2, shots=shots)\n",
        "    print(f\"dynamic_dd_meas2: {job_dynamic_dd_meas2.job_id()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "273fa3a0",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-results-in-desired-classical-format\" />\n",
        "\n",
        "## Fase 4: Post-elaborazione e restituzione dei risultati nel formato classico desiderato\n",
        "\n",
        "Una volta completati i processi, possiamo ricavare la durata del ciclo dai metadati dei risultati dei processi e visualizzare le informazioni relative alla pianificazione del ciclo. Per ulteriori informazioni sulla visualizzazione delle informazioni di pianificazione di un circuito, consultare [questa pagina](/docs/guides/qiskit-runtime-circuit-timing).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "bc16418f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Circuit durations is reported in the unit of `dt`\n",
        "# which can be retrieved from `Backend` object\n",
        "unitary_durations = [\n",
        "    job_unitary.result()[i].metadata[\"compilation\"][\"scheduler_timing\"][\n",
        "        \"circuit_duration\"\n",
        "    ]\n",
        "    for i in depths\n",
        "]\n",
        "\n",
        "dynamic_durations = [\n",
        "    job_dynamic.result()[i].metadata[\"compilation\"][\"scheduler_timing\"][\n",
        "        \"circuit_duration\"\n",
        "    ]\n",
        "    for i in depths\n",
        "]\n",
        "\n",
        "dynamic_durations_meas2 = [\n",
        "    job_dynamic_meas2.result()[i].metadata[\"compilation\"][\"scheduler_timing\"][\n",
        "        \"circuit_duration\"\n",
        "    ]\n",
        "    for i in depths\n",
        "]\n",
        "\n",
        "result_dd = job_dynamic_dd.result()[1]\n",
        "circuit_schedule_dd = result_dd.metadata[\"compilation\"][\"scheduler_timing\"][\n",
        "    \"timing\"\n",
        "]\n",
        "\n",
        "# to visualize the circuit schedule, one can show the figure below\n",
        "fig_dd = draw_circuit_schedule_timing(\n",
        "    circuit_schedule=circuit_schedule_dd,\n",
        "    included_channels=None,\n",
        "    filter_readout_channels=False,\n",
        "    filter_barriers=False,\n",
        "    width=1000,\n",
        ")\n",
        "\n",
        "# Save to a file since the figure is large\n",
        "fig_dd.write_html(\"scheduler_timing_dd.html\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bee08b77",
      "metadata": {},
      "source": [
        "Tracciamo la durata dei circuiti unitari e dei circuiti dinamici. Dal grafico sottostante, possiamo vedere che, nonostante il tempo necessario per le misurazioni a metà circuito e le operazioni classiche, l'implementazione dinamica del circuito con `measure_2` comporta durate del circuito paragonabili a quelle dell'implementazione unitaria.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "639221e6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x12bfde270>"
            ]
          },
          "execution_count": 33,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/639221e6-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# visualize circuit durations\n",
        "\n",
        "\n",
        "def convert_dt_to_microseconds(circ_duration: List, backend_dt: float):\n",
        "    dt = backend_dt * 1e6  # dt in microseconds\n",
        "    return list(map(lambda x: x * dt, circ_duration))\n",
        "\n",
        "\n",
        "dt = backend.target.dt\n",
        "plt.plot(\n",
        "    depths,\n",
        "    convert_dt_to_microseconds(unitary_durations, dt),\n",
        "    color=\"#be95ff\",\n",
        "    linestyle=\":\",\n",
        "    label=\"unitary\",\n",
        ")\n",
        "plt.plot(\n",
        "    depths,\n",
        "    convert_dt_to_microseconds(dynamic_durations, dt),\n",
        "    color=\"#ff7eb6\",\n",
        "    linestyle=\"-.\",\n",
        "    label=\"dynamic\",\n",
        ")\n",
        "plt.plot(\n",
        "    depths,\n",
        "    convert_dt_to_microseconds(dynamic_durations_meas2, dt),\n",
        "    color=\"#ff7eb6\",\n",
        "    linestyle=\"-.\",\n",
        "    marker=\"s\",\n",
        "    mfc=\"none\",\n",
        "    label=\"dynamic w/ meas2\",\n",
        ")\n",
        "\n",
        "plt.xlabel(\"Trotter steps\")\n",
        "plt.ylabel(r\"Circuit durations in $\\mu$s\")\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3c2bd06f",
      "metadata": {},
      "source": [
        "Una volta completati i lavori, recuperiamo i dati riportati di seguito e calcoliamo la magnetizzazione media stimata dagli osservabili `observables_unitary` o `observables_dynamic` che abbiamo costruito in precedenza.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "f4947049",
      "metadata": {},
      "outputs": [],
      "source": [
        "runs = {\n",
        "    \"unitary\": (\n",
        "        job_unitary,\n",
        "        [observables_unitary] * len(circuits_unitary),\n",
        "    ),\n",
        "    \"unitary_dd\": (\n",
        "        job_unitary_dd,\n",
        "        [observables_unitary] * len(circuits_unitary),\n",
        "    ),\n",
        "    # Omitting Dyn w/o DD and Dynamic w/ DD plots for better readability\n",
        "    # \"dynamic\": (job_dynamic, observables_dynamic),\n",
        "    # \"dynamic_dd\": (job_dynamic_dd, observables_dynamic),\n",
        "    \"dynamic_meas2\": (job_dynamic_meas2, observables_dynamic),\n",
        "    \"dynamic_dd_meas2\": (\n",
        "        job_dynamic_dd_meas2,\n",
        "        observables_dynamic,\n",
        "    ),\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "583d417e",
      "metadata": {},
      "outputs": [],
      "source": [
        "data_dict = {}\n",
        "for key, (job, obs) in runs.items():\n",
        "    data = []\n",
        "    for i in range(points):\n",
        "        data.append(\n",
        "            [\n",
        "                job.result()[ind].data[\"meas\"].expectation_values(obs[ind])[i]\n",
        "                for ind in depths\n",
        "            ]\n",
        "        )\n",
        "    data_dict[key] = data"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "507073d9",
      "metadata": {},
      "source": [
        "Di seguito tracciamo la magnetizzazione di spin in funzione dei passi di Trotter a diversi valori di $\\theta$, corrispondenti a diverse intensità del campo magnetico locale. Tracciamo sia i risultati della simulazione MPS precalcolati per i circuiti ideali unitari, sia i risultati sperimentali ottenuti da quanto segue:\n",
        "\n",
        "1. esecuzione dei circuiti unitari con DD\n",
        "2. eseguendo i circuiti dinamici con DD e `MidCircuitMeasure`\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "662239cf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/dc-hex-ising/extracted-outputs/662239cf-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(10, 6))\n",
        "\n",
        "colors = [\"#0f62fe\", \"#be95ff\", \"#ff7eb6\"]\n",
        "for i in range(points):\n",
        "    plt.plot(\n",
        "        depths,\n",
        "        data_sim[i],\n",
        "        color=colors[i],\n",
        "        linestyle=\"solid\",\n",
        "        label=f\"θ={pi_check(i*max_angle/(points-1))} (MPS)\",\n",
        "    )\n",
        "    # plt.plot(\n",
        "    #     depths,\n",
        "    #     data_dict[\"unitary\"][i],\n",
        "    #     color=colors[i],\n",
        "    #     linestyle=\":\",\n",
        "    #     label=f\"θ={pi_check(i*max_angle/(points-1))} (Unitary)\",\n",
        "    # )\n",
        "\n",
        "    plt.plot(\n",
        "        depths,\n",
        "        data_dict[\"unitary_dd\"][i],\n",
        "        color=colors[i],\n",
        "        marker=\"o\",\n",
        "        mfc=\"none\",\n",
        "        linestyle=\":\",\n",
        "        label=f\"θ={pi_check(i*max_angle/(points-1))} (Unitary w/DD)\",\n",
        "    )\n",
        "\n",
        "    # Omitting Dyn w/o DD and Dynamic w/ DD plots for better readability\n",
        "    # plt.plot(\n",
        "    #     depths,\n",
        "    #     data_dict[\"dynamic\"][i],\n",
        "    #     color=colors[i],\n",
        "    #     linestyle=\"-.\",\n",
        "    #     label=f\"θ={pi_check(i*max_angle/(points-1))} (Dyn w/o DD)\",\n",
        "    # )\n",
        "    # plt.plot(\n",
        "    #     depths,\n",
        "    #     data_dict[\"dynamic_dd\"][i],\n",
        "    #     marker=\"D\",\n",
        "    #     mfc=\"none\",\n",
        "    #     color=colors[i],\n",
        "    #     linestyle=\"-.\",\n",
        "    #     label=f\"θ={pi_check(i*max_angle/(points-1))} (Dynamic w/ DD)\",\n",
        "    # )\n",
        "\n",
        "    # plt.plot(\n",
        "    #     depths,\n",
        "    #     data_dict[\"dynamic_meas2\"][i],\n",
        "    #     color=colors[i],\n",
        "    #     marker=\"s\",\n",
        "    #     mfc=\"none\",\n",
        "    #     linestyle=':',\n",
        "    #     label=f\"θ={pi_check(i*max_angle/(points-1))} (Dynamic w/ MidCircuitMeas)\",\n",
        "    # )\n",
        "\n",
        "    plt.plot(\n",
        "        depths,\n",
        "        data_dict[\"dynamic_dd_meas2\"][i],\n",
        "        color=colors[i],\n",
        "        marker=\"*\",\n",
        "        markersize=8,\n",
        "        linestyle=\":\",\n",
        "        label=f\"θ={pi_check(i*max_angle/(points-1))} \"\n",
        "        f\"(Dynamic w/ DD & MidCircuitMeas)\",\n",
        "    )\n",
        "\n",
        "\n",
        "plt.xlabel(\"Trotter steps\", fontsize=16)\n",
        "plt.ylabel(\"Average magnetization\", fontsize=16)\n",
        "plt.xticks(rotation=45)\n",
        "handles, labels = plt.gca().get_legend_handles_labels()\n",
        "plt.legend(\n",
        "    handles,\n",
        "    labels,\n",
        "    loc=\"upper right\",\n",
        "    bbox_to_anchor=(1.46, 1.0),\n",
        "    shadow=True,\n",
        "    ncol=1,\n",
        ")\n",
        "plt.title(\n",
        "    f\"{hex_rows}x{hex_cols} hex ring, {num_qubits} data qubits, \"\n",
        "    f\"{len(ancilla)} ancilla qubits \\n{backend.name}: Sampler\"\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aead1034",
      "metadata": {},
      "source": [
        "Quando confrontiamo i risultati sperimentali con la simulazione, vediamo che l'implementazione del circuito dinamico (linea tratteggiata con stelle) ha complessivamente prestazioni migliori rispetto all'implementazione unitaria standard (linea tratteggiata con cerchi). In sintesi, presentiamo i circuiti dinamici come soluzione per simulare i modelli di spin di Ising su un reticolo a nido d'ape, una topologia che non è nativa dell'hardware. La soluzione del circuito dinamico consente interazioni ZZ tra qubit che non sono vicini più prossimi, con una profondità di gate a due qubit più breve rispetto all'uso dei gate SWAP, al costo di introdurre qubit ancilla aggiuntivi e operazioni classiche di feedforward.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52fb43e1",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Riferimenti\n",
        "\n",
        "\\[1] Quantum computing con Qiskit, di Javadi-Abhari, A., Treinish, M., Krsulich, K., Wood, C.J Lishman, J., Gacon, J., Martiel, S., Nation, P.D Bishop, L.S Cross, A.W. e Johnson, B.R., 2024. arXiv preprint [arXiv:2405.08810 (2024)](https://arxiv.org/abs/2405.08810)\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": 450
  },
  "nbformat": 4,
  "nbformat_minor": 5
}