{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "44ef87b3",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simulación del hamiltoniano de Ising con circuitos dinámicos\"\n",
        "description: \"Tutorial que muestra circuitos dinámicos a escala industrial utilizando una simulación del modelo hexagonal de Ising con efecto de impulso\"\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",
        "# Simulación del hamiltoniano de Ising con circuitos dinámicos\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2ae52bd4",
      "metadata": {},
      "source": [
        "*Estimación de uso: 7.5 minutos en un procesador Heron r3. (NOTA: Esto es solo una estimación. El tiempo de ejecución puede variar.*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c76f2627",
      "metadata": {},
      "source": [
        "Los circuitos dinámicos son circuitos con alimentación directa clásica; en otras palabras, son mediciones a mitad del circuito seguidas de operaciones lógicas clásicas que determinan operaciones cuánticas condicionadas por la salida clásica. En este tutorial, simulamos el modelo de Ising con efecto Kicked en una red hexagonal de espines y utilizamos circuitos dinámicos para realizar interacciones más allá de la conectividad física del hardware.\n",
        "\n",
        "El modelo de Ising ha sido ampliamente estudiado en diversas áreas de la física. Modela espines que experimentan interacciones de Ising entre los sitios de la red, así como impulsos del campo magnético local en cada sitio. La evolución temporal trotterizada de los espines considerados en este tutorial, tomada de [\\[1\\]](#references), viene dada por la siguiente unidad:\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",
        "Para investigar la dinámica de espín, estudiamos la magnetización media de los espines en cada sitio en función de los pasos de Trotter. Por lo tanto, construimos la siguiente observable:\n",
        "\n",
        "$$\n",
        "\\langle O\\rangle =  \\frac{1}{N} \\sum_i \\langle Z_i \\rangle\n",
        "$$\n",
        "\n",
        "Para realizar la interacción ZZ entre los sitios de la red, presentamos una solución que utiliza la función de circuito dinámico, lo que da lugar a una profundidad de dos qubits significativamente menor en comparación con el método de enrutamiento estándar con puertas SWAP. Por otro lado, las operaciones clásicas de alimentación directa en circuitos dinámicos suelen tener tiempos de ejecución más largos que las puertas cuánticas; por lo tanto, los circuitos dinámicos tienen limitaciones y compensaciones. También presentamos una forma de añadir una secuencia de desacoplamiento dinámico en los qubits inactivos durante la operación clásica de alimentación directa utilizando la duración [del estiramiento](/docs/guides/stretch).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a6c0af3b",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de comenzar este tutorial, asegúrate de tener instalado lo siguiente:\n",
        "\n",
        "* Qiskit SDK v2.0 o posterior con soporte [de visualización](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.37 o posterior con soporte de visualización (`pip install 'qiskit-ibm-runtime[visualization]'`)\n",
        "* Biblioteca de gráficos 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",
        "## Configurar\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",
        "## Paso 1: Asignar entradas clásicas a un circuito cuántico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "188f83ae",
      "metadata": {},
      "source": [
        "Comenzamos definiendo la red que se va a simular. Elegimos trabajar con la red de panal (también llamada hexagonal), que es un grafo plano con nodos de grado 3. Aquí especificamos el tamaño de la red y los parámetros relevantes del circuito que nos interesan en la dinámica trotterizada. Simulamos la evolución temporal trotterizada bajo el modelo de Ising con tres valores diferentes de $\\theta$ del campo magnético local.\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": [
        "Comencemos con un pequeño ejemplo de prueba:\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": [
        "Utilizaremos el pequeño ejemplo para la ilustración y la simulación. A continuación, también construimos un ejemplo grande para mostrar que el flujo de trabajo se puede ampliar a tamaños grandes.\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",
        "### Construir circuitos unitarios\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f4b421fa",
      "metadata": {},
      "source": [
        "Una vez especificados el tamaño del problema y los parámetros, ya estamos listos para construir el circuito parametrizado que simula la evolución temporal trotterizada de $U(\\theta)$ con diferentes pasos de Trotter, especificados por el `depth` argumento. El circuito que construimos tiene capas alternas de puertas `Rx` ( $\\theta$ ) y `Rzz` puertas. Las `Rzz` puertas realizan las interacciones ZZ entre espines acoplados, que se colocarán entre cada sitio de la red especificado por el `layer_edges` argumento.\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": [
        "Visualice el pequeño circuito de prueba:\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": [
        "Del mismo modo, construya los circuitos unitarios del ejemplo grande en diferentes pasos de Trotter y el observable para estimar el valor esperado.\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",
        "### Implementación de circuitos dinámicos\n",
        "\n",
        "Esta sección muestra la implementación del circuito dinámico principal para simular la misma evolución temporal trotterizada. Tenga en cuenta que la red de panal que queremos simular no coincide con la pesada red de los qubits de hardware. Una forma sencilla de mapear el circuito al hardware es introducir una serie de operaciones SWAP para colocar los qubits que interactúan uno al lado del otro, con el fin de realizar la interacción ZZ. Aquí destacamos un enfoque alternativo que utiliza circuitos dinámicos como solución, lo que ilustra que podemos utilizar la combinación de computación cuántica y computación clásica en tiempo real dentro de un circuito en Qiskit para realizar interacciones más allá del vecino más cercano.\n",
        "\n",
        "En la implementación del circuito dinámico, la interacción ZZ se implementa de manera eficaz utilizando qubits auxiliares, mediciones a mitad del circuito y alimentación directa. Para entender esto, hay que tener en cuenta que las rotaciones ZZ aplican un factor de fase $e^{i\\theta}$ al estado en función de su paridad. Para dos qubits, los estados de base computacional son $|00\\rangle$, $|01\\rangle$, $|10\\rangle$ y $|11\\rangle$. La puerta de rotación ZZ aplica un factor de fase a los estados $|01\\rangle$ y $|10\\rangle$ cuya paridad (el número de unos en el estado) es impar y deja sin cambios los estados de paridad par. A continuación se describe cómo podemos implementar eficazmente interacciones ZZ en dos qubits utilizando circuitos dinámicos.\n",
        "\n",
        "1. Calcular la paridad en un qubit auxiliar: en lugar de aplicar directamente ZZ a dos qubits, introducimos un tercer qubit, el qubit auxiliar, para almacenar la información de paridad de los dos qubits de datos. Entrelazamos el ancilla con cada qubit de datos utilizando puertas CX desde el qubit de datos al qubit ancilla.\n",
        "\n",
        "2. Aplica una rotación Z de un solo qubit al qubit auxiliar: esto se debe a que el qubit auxiliar tiene la información de paridad de los dos qubits de datos, lo que implementa eficazmente la rotación ZZ en los qubits de datos.\n",
        "\n",
        "3. Mide el qubit auxiliar en la base X: este es el paso clave que colapsa el estado del qubit auxiliar, y el resultado de la medición nos dice lo que ha sucedido:\n",
        "\n",
        "   * Medida 0: cuando se observa un resultado 0, significa que hemos aplicado correctamente una rotación e $ZZ(\\theta)$ a a nuestros qubits de datos.\n",
        "\n",
        "   * Medida 1: cuando se observa un resultado 1, hemos aplicado en su lugar $ZZ(\\theta + \\pi)$.\n",
        "\n",
        "4. Aplicar puerta de corrección al medir 1: Si medimos 1, aplicamos puertas Z a los qubits de datos para «corregir» la fase e $\\pi$ e adicional.\n",
        "\n",
        "El circuito resultante es el siguiente:\n",
        "\n",
        "![implementación dinámica](https://quantum.cloud.ibm.com/docs/images/tutorials/dc-hex-ising/circuit-1.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9872fed2",
      "metadata": {},
      "source": [
        "Cuando adoptamos este enfoque para simular una red de panal, el circuito resultante se integra perfectamente en el hardware con una red hexagonal densa: todos los qubits de datos residen en los sitios degree-3 de la red, que forma una red hexagonal. Cada par de qubits de datos comparte un qubit auxiliar que reside en un sitio e degree-2. A continuación, construimos la red de qubits para la implementación del circuito dinámico, introduciendo qubits auxiliares (mostrados en los círculos morados más oscuros).\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": [
        "Visualiza la red hexagonal pesada para los qubits de datos y los qubits auxiliares a pequeña escala:\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": [
        "A continuación, construimos el circuito dinámico para la evolución temporal trotterizada. Las `RZZ` puertas se sustituyen por la implementación del circuito dinámico utilizando los pasos descritos anteriormente.\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",
        "#### Desacoplamiento dinámico (DD) y soporte para `stretch` duración\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f08bda1",
      "metadata": {},
      "source": [
        "Una advertencia sobre el uso de la implementación de circuitos dinámicos para realizar la interacción ZZ es que la medición en medio del circuito y las operaciones clásicas de alimentación directa suelen tardar más tiempo en ejecutarse que las puertas cuánticas. Para suprimir la decoherencia de los qubits durante el tiempo de inactividad necesario para que se produzcan las operaciones clásicas, añadimos una secuencia [de desacoplamiento dinámico](/docs/guides/error-mitigation-and-suppression-techniques#dynamical-decoupling) (DD) después de la operación de medición en los qubits auxiliares y antes de la operación Z condicional en el qubit de datos, antes de la `if_test` instrucción.\n",
        "\n",
        "La secuencia DD se añade mediante la función `add_stretch_dd()`, que utiliza el [`stretch` duraciones](/docs/guides/stretch) para determinar los intervalos de tiempo entre las puertas DD. Una `stretch` duración es una forma de especificar una duración de tiempo extensible para la `delay` operación, de modo que la duración del retraso pueda aumentar hasta llenar el tiempo de inactividad del qubit. Las variables de duración especificadas por `stretch` se resuelven en tiempo de compilación en las duraciones deseadas que satisfacen una determinada restricción. Esto resulta muy útil cuando la sincronización de las secuencias DD es esencial para lograr un buen rendimiento en la supresión de errores. Para obtener más detalles sobre el `stretch` tipo, consulte la [OpenQASM](https://openqasm.com/language/delays.html#duration-and-stretch-types) documentación. Actualmente, la compatibilidad con el `stretch` tipo en Qiskit Runtime es experimental. Para obtener más información sobre las restricciones de uso, consulte [la sección](/docs/guides/stretch#qiskit-runtime-limitations) de limitaciones de la `stretch` documentación.\n",
        "\n",
        "Utilizando las funciones definidas anteriormente, construimos los circuitos de evolución temporal trotterizados, con y sin DD, y los observables correspondientes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "997419c8",
      "metadata": {},
      "source": [
        "Comenzamos visualizando el circuito dinámico de un pequeño ejemplo:\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": [
        "Del mismo modo, construya los circuitos dinámicos para el ejemplo 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",
        "## Paso 2: Optimizar el problema para la ejecución del hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "86642d2b",
      "metadata": {},
      "source": [
        "Ahora estamos listos para transpilear el circuito al hardware. Transpilamos tanto la implementación del estándar unitario como la implementación del circuito dinámico al hardware.\n",
        "\n",
        "Para transpilar al hardware, primero instanciamos el backend. Si está disponible, elegiremos un backend que admita la [`MidCircuitMeasure`](/docs/guides/measure-qubits) instrucción (`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",
        "### Transpilación para circuitos dinámicos\n",
        "\n",
        "En primer lugar, transpilamos los circuitos dinámicos, con y sin añadir la secuencia DD. Para garantizar que utilizamos el mismo conjunto de qubits físicos en todos los circuitos y obtener resultados más consistentes, primero transpilamos el circuito una vez y, a continuación, utilizamos su diseño para todos los circuitos posteriores, especificados por [`initial_layout`](/docs/api/qiskit/qiskit.transpiler.TranspileLayout#initial_layout) en el gestor de pasadas. A continuación, construimos los [bloques primitivos unificados](/docs/guides/primitive-input-output) (PUB) como entrada primitiva del muestreador.\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": [
        "Podemos visualizar la disposición de los qubits del circuito transpuesto a continuación. Los círculos negros muestran los qubits de datos y los qubits auxiliares utilizados en la implementación del circuito dinámico.\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",
        "  Si obtienes errores sobre `neato` no encontrado desde `plot_circuit_layout()`, asegúrate de tener el `graphviz` paquete instalado y disponible en tu PATH. Si se instala en una ubicación no predeterminada (por ejemplo, utilizando `homebrew` en MacOS ), es posible que tenga que actualizar su variable `PATH` de entorno. Esto se puede hacer dentro de este cuaderno utilizando lo siguiente:\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",
        "#### Transpilar utilizando `MidCircuitMeasure`\n",
        "\n",
        "`MidCircuitMeasure` es una ampliación de las operaciones de medición disponibles, calibrada específicamente para realizar [mediciones en el punto medio del circuito](/docs/guides/execute-dynamic-circuits#midcircuit). La `MidCircuitMeasure` instrucción se traduce a la `measure_2` instrucción compatible con los backends. Ten en cuenta que no `measure_2` es compatible con todos los backends. Puedes utilizar `service.backends(filters=lambda b: \"measure_2\" in b.supported_instructions)` para encontrar backends que lo admitan. A continuación, mostramos cómo transpilar el circuito para que las mediciones en tiempo real definidas en él se ejecuten mediante la `MidCircuitMeasure` operación, siempre que el backend la admita.\n",
        "\n",
        "A continuación, imprimimos la duración de la `measure_2` instrucción y la instrucción `measure` estándar.\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",
        "### Transpilación para circuitos unitarios\n",
        "\n",
        "Para establecer una comparación justa entre los circuitos dinámicos y su contraparte unitaria, utilizamos el mismo conjunto de qubits físicos utilizados en los circuitos dinámicos para los qubits de datos como diseño para transpilear los circuitos unitarios.\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": [
        "Visualizamos la disposición de los qubits de los circuitos unitarios transpilados. Los círculos negros indican los qubits físicos utilizados para transpilar los circuitos unitarios y sus índices corresponden a los índices de los qubits virtuales. Al comparar esto con el diseño trazado para los circuitos dinámicos, podemos confirmar que los circuitos unitarios utilizan el mismo conjunto de qubits físicos que los qubits de datos en los circuitos dinámicos.\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": [
        "Ahora añadimos la secuencia DD a los circuitos transpilados y construimos los PUB correspondientes para el envío de trabajos.\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",
        "### Compara la profundidad de puerta de dos qubits de circuitos unitarios y dinámicos\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": [
        "La principal ventaja del circuito basado en mediciones es que, al implementar múltiples interacciones ZZ, las capas CX se pueden paralelizar y las mediciones se pueden realizar simultáneamente. Esto se debe a que todas las interacciones ZZ son conmutativas, por lo que el cálculo se puede realizar con una profundidad de medición 1. Tras transpilación de los circuitos, observamos que el enfoque de circuito dinámico produce una profundidad de dos qubits significativamente menor que el enfoque unitario estándar, con la salvedad de que la medición adicional a mitad del circuito y la alimentación directa clásica requieren tiempo e introducen sus propias fuentes de error.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "206a7278",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Paso 3: Ejecutar utilizando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "53a3e876",
      "metadata": {},
      "source": [
        "<span id=\"local-testing-mode\" />\n",
        "\n",
        "#### Modo de prueba local\n",
        "\n",
        "Antes de enviar los trabajos al hardware, podemos ejecutar una pequeña simulación de prueba del circuito dinámico utilizando el [modo de prueba local](/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",
        "#### Simulación MPS\n",
        "\n",
        "Para circuitos grandes, podemos utilizar el simulador `matrix_product_state` (MPS), que proporciona un resultado aproximado al valor esperado según la dimensión de enlace elegida. Posteriormente, utilizamos los resultados de la simulación MPS como referencia para comparar los resultados del 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": [
        "Con los circuitos y observables preparados, ahora los ejecutamos en hardware utilizando la primitiva Sampler.\n",
        "\n",
        "Aquí presentamos tres trabajos para `unitary_pubs`, `dynamic_pubs`, y `dynamic_pubs_dd`. Cada uno es una lista de circuitos parametrizados correspondientes a nueve pasos Trotter diferentes con tres parámetros e $\\theta$ es diferentes.\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",
        "## Paso 4: Procesar posteriormente y devolver los resultados en el formato clásico deseado\n",
        "\n",
        "Una vez finalizados los trabajos, podemos obtener la duración del circuito a partir de los metadatos de los resultados de los trabajos y visualizar la información sobre el calendario del circuito. Para obtener más información sobre cómo visualizar la información de programación de un circuito, consulta [esta página](/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": [
        "Trazamos las duraciones de los circuitos unitarios y los circuitos dinámicos. En el gráfico siguiente podemos ver que, a pesar del tiempo necesario para las mediciones a mitad del circuito y las operaciones clásicas, la implementación dinámica del circuito con `measure_2` da como resultado duraciones del circuito comparables a las de la implementación 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 vez completados los trabajos, recuperamos los datos que se muestran a continuación y calculamos la magnetización media estimada por las observables `observables_unitary` o `observables_dynamic` que hemos construido anteriormente.\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": [
        "A continuación, representamos gráficamente la magnetización de espín en función de los pasos de Trotter a diferentes valores de $\\theta$, correspondientes a diferentes intensidades del campo magnético local. Trazamos tanto los resultados de la simulación MPS precalculados para los circuitos ideales unitarios, como los resultados experimentales de lo siguiente:\n",
        "\n",
        "1. ejecutar los circuitos unitarios con DD\n",
        "2. ejecutando los circuitos dinámicos con DD y `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": [
        "Cuando comparamos los resultados experimentales con la simulación, vemos que la implementación del circuito dinámico (línea punteada con estrellas) tiene, en general, un mejor rendimiento que la implementación unitaria estándar (línea punteada con círculos). En resumen, presentamos los circuitos dinámicos como una solución para simular modelos de espín de Ising en una red hexagonal, una topología que no es nativa del hardware. La solución de circuito dinámico permite interacciones ZZ entre qubits que no son vecinos más cercanos, con una profundidad de puerta de dos qubits más corta que la que se obtiene con puertas SWAP, a costa de introducir qubits auxiliares adicionales y operaciones clásicas de alimentación directa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52fb43e1",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referencias\n",
        "\n",
        "\\[1] Computación cuántica con Qiskit, por 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. y Johnson, B.R 2024. « arXiv[ » (](https://arxiv.org/abs/2405.08810) Preimpresión) arXiv:2405.08810 (2024)\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
}