{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "39a1fd9b-a17f-486e-aa7a-38d46b867984",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Utilidad I\"\n",
        "description: \"En esta lección, ejecutamos un circuito a escala industrial que aparece en lo que informalmente llamamos el artículo sobre servicios públicos, publicado en Nature Vol. 618, 15 de junio de 2023.\"\n",
        "---\n",
        "\n",
        "<span id=\"utility-scale-experiment-i\" />\n",
        "\n",
        "# Experimento a escala industrial I\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Tamiya Onodera (5 de julio de 2024)\n",
        "\n",
        "  [Descargue el pdf](https://ibm.ent.box.com/public/static/k38xhk5yaqy9b3gjdmu0ynhnd9i4c3e1.zip) de la conferencia original. Tenga en cuenta que algunos fragmentos de código pueden quedar obsoletos, ya que se trata de imágenes estáticas.\n",
        "\n",
        "  *El tiempo aproximado de QPU para ejecutar este experimento es de 45 segundos.*\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "f0d73c71-11ba-4dcb-b230-ecea500e8732",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction-to-the-utility-paper\" />\n",
        "\n",
        "## 1. Introducción al documento de utilidad\n",
        "\n",
        "En esta lección, ejecutamos un circuito a escala de utilidad que aparece en lo que llamamos informalmente [\"el artículo de utilidad\"](https://www.nature.com/articles/s41586-023-06096-3) publicado en Nature Vol 618, 15 de junio de 2023. El artículo trata de la evolución temporal del modelo de Ising de campo transversal 2D. En particular, consideran la dinámica temporal del hamiltoniano,\n",
        "\n",
        "$$\n",
        "H = H_{ZZ} + H_X = - J \\sum_{(i,j)} Z_i Z_j + h \\sum_{i} X_i\n",
        "$$\n",
        "\n",
        "en el que $J > 0$ es el acoplamiento de los espines del vecino más cercano con $i < j$ y $h$ es el campo transversal global.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "c92cf6bb-df3e-4020-b980-edf17188e096",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "Simulan la dinámica del espín a partir de un estado inicial mediante la descomposición de Trotter de primer orden del operador tiempo-evolución,\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        " \\exp(-i H_{ZZ} \\delta t) &= \\prod_{(i,j)} \\exp (i J \\delta t Z_i Z_j)  = \\prod_{(i,j)} \\mathrm{R}_{Z_i Z_j} ( - 2 J \\delta t) \\\\\n",
        " \\exp(-i H_X \\delta t) &= \\prod_{i} \\exp (-i h \\delta t X_i )  = \\prod_{i} \\mathrm{R}_{X_i} ( 2 h \\delta t)\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "en el que el tiempo de evolución $T$ se discretiza en $T / \\delta t$ pasos de Trotter y $\\mathrm{R}_{Z_i Z_j}(\\theta_J)$ y $\\mathrm{R}_{X_i}(\\theta_h)$ son $ZZ$ y $X$ puertas de rotación, respectivamente.\n",
        "\n",
        "Realizaron experimentos en un procesador IBM Quantum® Eagle, que es un dispositivo de 127 qubits con conectividad heavy-hex, aplicando interacciones $X$ a todos los qubits e interacciones $ZZ$ para todos los bordes del mapa de acoplamiento.  Tenga en cuenta que todas las interacciones de $ZZ$ no pueden aplicarse simultáneamente debido a la \"dependencia de los datos\".  De ahí que **coloreen el mapa de acoplamiento** para agruparlos en capas. A los de una capa se les asigna el mismo color y pueden aplicarse en paralelo.\n",
        "\n",
        "Además, por simplicidad experimental, se centraron en el caso $\\theta_J=-\\pi /2$.\n",
        "\n",
        "La aportación novedosa del artículo es que construyeron circuitos cuánticos a una escala superior a la simulación de vectores de estado, los ejecutaron en ordenadores cuánticos ruidosos y consiguieron extraer resultados fiables. Es decir, demostraron la utilidad de los ordenadores cuánticos ruidosos. Para ello, aplicaron la extrapolación de ruido cero (ZNE) con amplificación probabilística de errores (PEA) para mitigar los errores de los dispositivos ruidosos.\n",
        "\n",
        "A partir de entonces, llamamos a estos experimentos y circuitos \"a escala de servicios públicos\".\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fcb359b4-469a-4e37-b260-8bf5044d1f60",
      "metadata": {},
      "source": [
        "<span id=\"11-your-goal\" />\n",
        "\n",
        "### 1.1 Tu objetivo\n",
        "\n",
        "Tu objetivo en esta lección es construir un circuito a escala de servicios públicos y ejecutarlo en un procesador Eagle.  Está fuera del alcance de este cuaderno extraer resultados fiables, en parte porque PEA es una característica experimental de Qiskit en el momento de escribir esto y en parte porque aplicar ZNE con PEA llevará bastante tiempo.\n",
        "\n",
        "Concretamente, se le pide que construya y ponga en marcha el circuito correspondiente a la figura 4b del documento, y que trace los puntos \"no mitigados\" de los suyos.  Como ves, es un circuito de 127-qubit $\\times$ 60-capas (20 pasos Trotter) con el $\\langle Z_{62} \\rangle$ como observable.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a4aecf28-fc89-42db-ad8f-67e8afacff87",
      "metadata": {},
      "source": [
        "![image.png](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/utility-i/utility1.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b8c5a5ae-fc75-4fa9-9c70-2de36652f49a",
      "metadata": {},
      "source": [
        "¿Te parece gran cosa? No se preocupe. Las tres últimas lecciones de este curso sirven de trampolín. Para empezar, demostraremos un experimento a menor escala que consiste en construir y ejecutar en un dispositivo falso un circuito de 27-qubit $\\times$ de 6 capas (2 pasos de Trotter) con el $\\langle Z_{13} \\rangle$ como observable.\n",
        "\n",
        "Eso es todo en cuanto a la introducción. Emprendamos una aventura a escala comercial\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "ad57179f-240e-441d-b2e4-e531cd192b45",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "35fff60f-7c73-4492-ace9-ecdc8fd33b0c",
      "metadata": {},
      "outputs": [],
      "source": [
        "#!pip install qiskit_ibm_runtime\n",
        "#!pip install qiskit_aer"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2f7464cc-4190-400f-b947-4f38100b43d2",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "\n",
        "from qiskit import QuantumCircuit, transpile\n",
        "from qiskit.circuit import Parameter\n",
        "from qiskit.circuit.library import YGate\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    fake_provider,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "from qiskit_aer import AerSimulator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "acd628f1-e010-44b7-ba77-cceb3b4198c6",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "06556d62-08bc-4c43-a7d0-382b73ec239a",
      "metadata": {},
      "source": [
        "<span id=\"2-preparation\" />\n",
        "\n",
        "## 2. Preparación\n",
        "\n",
        "<span id=\"21-construct-rzz-$pi$-/-2\" />\n",
        "\n",
        "### 2.1 Construir RZZ(- $\\pi$ /2)\n",
        "\n",
        "En primer lugar, observe que la puerta RZZ requiere en general dos puertas $CX$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "d0b96520-3a4b-43c4-ade8-85cec68e3e09",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/d0b96520-3a4b-43c4-ade8-85cec68e3e09-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.circuit.library import RZZGate\n",
        "\n",
        "θ_h = Parameter(\"$\\\\theta_h$\")\n",
        "qc1 = QuantumCircuit(2)\n",
        "qc1.append(RZZGate(θ_h), [0, 1])\n",
        "qc1.decompose(reps=1).draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2770e3d2-d9d1-4bd5-8f00-8d8401f10438",
      "metadata": {},
      "source": [
        "Como se ha mencionado anteriormente, para este experimento nos centramos en la puerta RZZ con un ángulo específico, - $\\pi$ / 2.  Como se muestra en el artículo, puede realizarse con una sola puerta $CX$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "14b0ecb2-a8da-484a-bf49-606450bb074a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/14b0ecb2-a8da-484a-bf49-606450bb074a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc2 = QuantumCircuit(2)\n",
        "\n",
        "qc2.sdg([0, 1])\n",
        "qc2.append(YGate().power(1 / 2), [1])\n",
        "qc2.cx(0, 1)\n",
        "qc2.append(YGate().power(1 / 2).adjoint(), [1])\n",
        "\n",
        "qc2.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "269a5094-b4e6-4249-b9d5-ea25ded1767e",
      "metadata": {},
      "source": [
        "Definimos una puerta en términos de este circuito para futuras referencias.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "2b5fbb1a",
      "metadata": {},
      "outputs": [],
      "source": [
        "rzz = qc2.to_gate(label=\"RZZ\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d89cccac-13a8-49b5-9dab-c1e98f9735c8",
      "metadata": {},
      "source": [
        "Hagamos un uso aleatorio de la recién definida `rzz`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6753b9a4-90b1-4981-82b2-8177ac258af2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/6753b9a4-90b1-4981-82b2-8177ac258af2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "qc3 = QuantumCircuit(3)\n",
        "qc3.append(rzz, [0, 1])\n",
        "qc3.append(rzz, [0, 2])\n",
        "display(qc3.draw(\"mpl\"))\n",
        "# display(qc.decompose(reps=1).draw(\"mpl\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed9c4589-c996-4199-8dd7-19e8d144a384",
      "metadata": {},
      "source": [
        "Antes de seguir utilizándola, verifiquemos la equivalencia lógica de `qc1` (la puerta RZZ) para -pi/2 y nuestra recién definida puerta `rzz` o `qc2` :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "b84c6f00-d50a-4874-9fa4-0c81742654e8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.quantum_info import Operator\n",
        "\n",
        "op1 = Operator(qc1.assign_parameters([-np.pi / 2]))\n",
        "op2 = Operator(qc2)\n",
        "\n",
        "op1.equiv(op2)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ace1a98d-2ef7-4fe8-b67d-7799e7566f5a",
      "metadata": {},
      "source": [
        "<span id=\"22-color-the-coupling-map\" />\n",
        "\n",
        "### 2.2 Colorea el mapa de acoplamiento\n",
        "\n",
        "Estudiemos cómo coloreamos el mapa de acoplamiento de un backend.  Esto es necesario para agrupar las interacciones de $ZZ$ en capas.\n",
        "\n",
        "Para empezar, visualicemos el mapa de acoplamiento de un backend.  Obsérvese que los mapas de acoplamiento son hexagonales pesados para todos los dispositivos actuales de IBM Quantum.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "84e11b8e-bc74-4f3a-8538-d4d34d069e0f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/84e11b8e-bc74-4f3a-8538-d4d34d069e0f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "backend.coupling_map.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5fbfce4-c8cc-49fe-831d-31ad59f84c08",
      "metadata": {},
      "source": [
        "Para colorear un mapa de acoplamiento, utilizamos [rustworkx](https://www.rustworkx.org/), que es un paquete de Python para trabajar con grafos y redes complejas.  Proporciona múltiples algoritmos de coloreado, que son todos heurísticos y, por tanto, no garantizan que se encuentre un coloreado mínimo.\n",
        "\n",
        "Dicho esto, ya que los grafos pesados-hex son bipartitos, tomamos [grafo\\_bipartito\\_borde\\_color](https://www.rustworkx.org/apiref/rustworkx.graph_bipartite_edge_color.html), que debería encontrar una coloración mínima para estos grafos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "79d3af17-efa0-4e48-89c5-ad9a962cf83b",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "def color_coupling_map(backend):\n",
        "    graph = backend.coupling_map.graph\n",
        "    undirected_graph = graph.to_undirected(multigraph=False)\n",
        "    edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)\n",
        "    if edge_color_map is None:\n",
        "        edge_color_map = rx.graph_greedy_edge_color(undirected_graph)\n",
        "    # build a map from color to a list of edges\n",
        "    edge_index_map = undirected_graph.edge_index_map()\n",
        "    color_edges_map = {color: [] for color in edge_color_map.values()}\n",
        "    for edge_index, color in edge_color_map.items():\n",
        "        color_edges_map[color].append(\n",
        "            (edge_index_map[edge_index][0], edge_index_map[edge_index][1])\n",
        "        )\n",
        "    return edge_color_map, color_edges_map"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1a409faf-cb52-4c1c-95bd-beeca2cfd379",
      "metadata": {},
      "source": [
        "Los gráficos hexagonales pesados deben pintarse en tres colores.  Comprobémoslo para el mapa de acoplamiento anterior.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "5cf7f215-2155-498e-a8e8-acc51ccba363",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "ibm_strasbourg, 127-qubit device, 3 colors assigned.\n"
          ]
        }
      ],
      "source": [
        "edge_color_map, color_edges_map = color_coupling_map(backend)\n",
        "print(\n",
        "    f\"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "574bb71f-436e-499c-bb23-1adbab9445db",
      "metadata": {},
      "source": [
        "¡Sí que lo es!\n",
        "\n",
        "Por diversión, pintemos el mapa de acoplamiento a la coloración obtenida, utilizando la característica de visualización de rustworks.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "fb9b6915-5ace-4104-b81d-91666e689f0d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/fb9b6915-5ace-4104-b81d-91666e689f0d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "color_str_map = {0: \"green\", 1: \"red\", 2: \"blue\"}\n",
        "\n",
        "undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "for i in undirected_graph.edge_indices():\n",
        "    undirected_graph.get_edge_data_by_index(i)[\"color\"] = color_str_map[\n",
        "        edge_color_map[i]\n",
        "    ]\n",
        "\n",
        "rx.visualization.graphviz_draw(\n",
        "    undirected_graph, method=\"neato\", edge_attr_fn=lambda edge: {\"color\": edge[\"color\"]}\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4ae34f8d-6a46-4d81-bd0d-66a0d6fca588",
      "metadata": {},
      "source": [
        "<span id=\"3-solve-the-trotterized-time-evolution-of-a-2d-ising-model\" />\n",
        "\n",
        "## 3. Resolver la evolución temporal trotterizada de un modelo de Ising e 2D.\n",
        "\n",
        "Definamos una rutina para construir un circuito del papel de utilidad para la evolución temporal de un modelo 2D Ising.  La rutina toma tres parámetros, un backend, un entero que indica el número de pasos Trotter y un booleano que controla la inserción de la barrera.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "b9d5dfd9-997f-4d8c-af1f-7927df31bffb",
      "metadata": {},
      "outputs": [],
      "source": [
        "def get_utility_circuit(backend, num_steps: int, barrier: bool = False):\n",
        "    num_qubits = backend.num_qubits\n",
        "    _, color_edges_map = color_coupling_map(backend)\n",
        "    θ_h = Parameter(\"$\\\\theta_h$\")\n",
        "    qc = QuantumCircuit(num_qubits)\n",
        "\n",
        "    for i in range(num_steps):\n",
        "        qc.rx(θ_h, range(num_qubits))\n",
        "\n",
        "        for _, edge_list in color_edges_map.items():\n",
        "            for edge in edge_list:\n",
        "                qc.append(rzz, edge)\n",
        "\n",
        "        if barrier:\n",
        "            qc.barrier()\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e6689c89-8321-44e8-8983-82d6b0735751",
      "metadata": {},
      "source": [
        "Tenga en cuenta que ya hemos realizado manualmente el mapeo y enrutamiento de qubits para el circuito construido.  Por lo tanto, cuando transpilemos el circuito más tarde, no pediremos (**ni deberíamos** pedir) al transpilador que realice el mapeado y enrutado de qubits. Como verás enseguida, lo invocamos con el nivel de optimización como 1 y el método de diseño como \"trivial\".\n",
        "\n",
        "A continuación definimos una rutina sencilla para obtener la información sobre el circuito construido para una comprobación rápida.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c002120",
      "metadata": {},
      "outputs": [],
      "source": [
        "def get_circuit_info(qc: QuantumCircuit, reps: int = 0):\n",
        "    qc0 = qc.decompose(reps=reps)\n",
        "    return (\n",
        "        f\"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)\"\n",
        "        + \",  \"\n",
        "        + f\"\"\"Gate breakdown: {\", \".join([f\"{k.upper()} {v}\" for k, v in qc0.count_ops().items()])}\"\"\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30b69c52-09f0-4d5a-855c-e8f90acd0984",
      "metadata": {},
      "source": [
        "Ejercitemos estas rutinas.  Debería ver un circuito de 27 qubit $\\times$ 15 capas (5 pasos Trotter). Como el dispositivo falso tiene 28 aristas, debería haber 28\\*5 enredos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "af7bdb29-3bdb-4f6e-a53c-76ce935dff03",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/af7bdb29-3bdb-4f6e-a53c-76ce935dff03-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 15 layers (20-depth),  Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5\n",
            "27 qubits × 15 layers (60-depth),  Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5\n"
          ]
        }
      ],
      "source": [
        "backend = fake_provider.FakeTorontoV2()\n",
        "num_steps = 5\n",
        "qc = get_utility_circuit(backend, num_steps, True)\n",
        "\n",
        "display(qc.draw(output=\"mpl\", fold=-1))\n",
        "print(get_circuit_info(qc, reps=0))\n",
        "print(get_circuit_info(qc, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf038a4d-4482-4368-8171-8cb4cd828c51",
      "metadata": {},
      "source": [
        "<span id=\"4-solve-the-27-qubit-version-of-the-problem\" />\n",
        "\n",
        "## 4. Resuelve la versión de 27 qubits del problema.\n",
        "\n",
        "Ahora demostramos una versión a menor escala del experimento de utilidad. Construimos un circuito de 27-qubit $\\times$ de 6 capas (2 pasos Trotter) con $\\langle Z_{13} \\rangle$ como observable, y lo ejecutamos tanto en AerSimulator como en un dispositivo falso.\n",
        "\n",
        "Por supuesto, seguimos nuestro flujo de trabajo de cuatro pasos, \"patrón Qiskit\", que consiste en Mapear, Optimizar, Ejecutar y Postprocesar. Más concretamente,\n",
        "\n",
        "* Asignar entradas clásicas a un cálculo cuántico.\n",
        "* Optimizar circuitos para computación cuántica.\n",
        "* Ejecutar circuitos utilizando primitivas.\n",
        "* Post-proceso y devolución de resultados en formato clásico.\n",
        "\n",
        "A continuación, el paso de Map para crear un circuito para un experimento a menor escala. A continuación, tenemos un conjunto de Optimizar y Ejecutar para AerSimulator y otro para un dispositivo falso.  Por último, la etapa de postprocesamiento permite representar gráficamente los resultados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad5f3abd-0f25-47ec-9541-16fe83b7e710",
      "metadata": {},
      "source": [
        "<span id=\"41-step-1-map\" />\n",
        "\n",
        "### 4.1 Paso 1: Mapa\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "cd9c6774",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend = fake_provider.FakeTorontoV2()  # a 27 qubit fake device.\n",
        "num_steps = 2\n",
        "qc = get_utility_circuit(backend, num_steps)\n",
        "obs = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [13], 1)], num_qubits=backend.num_qubits\n",
        ")  # Falcon\n",
        "angles = [\n",
        "    0,\n",
        "    0.1,\n",
        "    0.2,\n",
        "    0.3,\n",
        "    0.4,\n",
        "    0.5,\n",
        "    0.6,\n",
        "    0.7,\n",
        "    0.8,\n",
        "    1.0,\n",
        "    np.pi / 2,\n",
        "]  # We try 11 angles for theta_h."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "81b39024-a7f4-4a7a-a543-59185def648b",
      "metadata": {},
      "source": [
        "<span id=\"42-steps-2-and-3-optimize-and-execute-simulator\" />\n",
        "\n",
        "### 4.2 Pasos 2 y 3: Optimizar y ejecutar (simulador)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "fa307165-fc74-4b6c-88c5-a2ac36341a3c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54\n",
            "27 qubits × 6 layers (16-depth),  Gate breakdown: U3 80, CX 56, R 54, U1 32, U 28\n"
          ]
        }
      ],
      "source": [
        "backend_sim = AerSimulator()\n",
        "transpiled_qc_sim = transpile(\n",
        "    qc, backend_sim, optimization_level=1, layout_method=\"trivial\"\n",
        ")\n",
        "transpiled_obs_sim = obs.apply_layout(layout=transpiled_qc_sim.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc_sim, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a101b40-5555-41d9-96b2-a8c986ea9903",
      "metadata": {},
      "source": [
        "Un usuario ejecutó la siguiente célula utilizando un MacBook Pro con procesador Intel Core i7 de cuatro núcleos a 2.3 GHz equipado con 32GB 3LPDDR4X RAM, que se ejecuta macOS 14.5.  Tomó 161ms en tiempo de pared. Cada portátil será ligeramente diferente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "178dd8c4-439b-4324-ba1f-687acc57a678",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "CPU times: user 231 ms, sys: 186 ms, total: 417 ms\n",
            "Wall time: 111 ms\n"
          ]
        }
      ],
      "source": [
        "%%time\n",
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend_sim)\n",
        "pub = (transpiled_qc_sim, transpiled_obs_sim, params)\n",
        "result_sim = estimator.run([pub]).result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "48b684f9-061a-49fd-acb1-3ed7b4bab1f2",
      "metadata": {},
      "source": [
        "<span id=\"43-steps-2-and-3-optimize-and-execute-fake-device\" />\n",
        "\n",
        "### 4.3 Pasos 2 y 3: Optimizar y ejecutar (dispositivo falso)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "f944eeae-8dc5-4ad6-97ad-92f95159956b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54\n",
            "27 qubits × 6 layers (49-depth),  Gate breakdown: SDG 324, U1 274, H 162, CX 56, U3 14\n"
          ]
        }
      ],
      "source": [
        "backend_fake = fake_provider.FakeTorontoV2()\n",
        "transpiled_qc_fake = transpile(\n",
        "    qc, backend_fake, optimization_level=1, layout_method=\"trivial\"\n",
        ")\n",
        "transpiled_obs_fake = obs.apply_layout(layout=transpiled_qc_fake.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc_fake, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59adfdfb-0954-42f7-9dd2-c819d04264b6",
      "metadata": {},
      "source": [
        "Cuando el mismo usuario ejecutó la siguiente celda con el mismo entorno que el anterior, tardó 2min 19s en Wall Time.  Ejecutar un circuito en un dispositivo falso invoca la simulación ruidosa, que tarda mucho más que la simulación exacta.   Le recomendamos que no ejecute un circuito más grande (como uno de 27 qubits $\\times$ 9 capas con 3 pasos Trotter) en un dispositivo falso.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "62d0e0bb-9acb-474a-8d19-b132013131a0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "CPU times: user 4min 42s, sys: 9.35 s, total: 4min 51s\n",
            "Wall time: 38.3 s\n"
          ]
        }
      ],
      "source": [
        "%%time\n",
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend_fake)\n",
        "pub = (transpiled_qc_fake, transpiled_obs_fake, params)\n",
        "result_fake = estimator.run([pub]).result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "181e5f61-2830-485c-bd22-a1d70b443ed9",
      "metadata": {},
      "source": [
        "<span id=\"44-step-4-post-process\" />\n",
        "\n",
        "### 4.4 Paso 4: Postprocesamiento\n",
        "\n",
        "Trazamos los resultados de simulaciones exactas y ruidosas. Puede ver los graves efectos del ruido en FakeToronto.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "4cf3cad0-e720-469c-a9ed-e397dae0f2f6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/4cf3cad0-e720-469c-a9ed-e397dae0f2f6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(angles, result_fake[0].data.evs, \"o\", label=\"Fake Device\")\n",
        "plt.plot(angles, result_sim[0].data.evs, \"o\", label=\"AerSimulator\")\n",
        "plt.xlabel(\"$\\\\mathrm{R_x}$ angle $\\\\theta_h$\")\n",
        "plt.title(\"$\\\\langle Z_{13} \\\\rangle$\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1b05966c-efe2-4eb4-b63c-43c07cf81f41",
      "metadata": {},
      "source": [
        "<span id=\"5-solve-the-127-qubit-version-of-the-problem\" />\n",
        "\n",
        "## 5. Resuelve la versión de 127 qubits del problema\n",
        "\n",
        "Su objetivo es llevar a cabo el experimento a escala comercial mencionado al principio. Creará y ejecutará un circuito de 127 qubits y 60 capas (20 pasos Trotter) con $\\langle Z_{62} \\rangle$ como observable. Le recomendamos que intente hacerlo usted mismo, utilizando el código de la versión de 27 qubits cuando proceda. Pero la solución está aquí.\n",
        "\n",
        "**Solución:**\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a18a42dd-5891-4b5f-8c3b-1bc7e1ae4a5e",
      "metadata": {},
      "source": [
        "<span id=\"51-step-1-map\" />\n",
        "\n",
        "### 5.1 Paso 1: Mapa\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8b900d9a-0351-426a-b25b-19e631fd257e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# backend_map = service.backend(\"ibm_brisbane\")\n",
        "backend_map = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "num_steps = 20\n",
        "qc = get_utility_circuit(backend_map, num_steps)\n",
        "obs = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [62], 1)], num_qubits=backend_map.num_qubits\n",
        ")  # Eagle\n",
        "angles = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, np.pi / 2]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "51ea0288-d8e9-4ee1-bbe8-dc24826a5026",
      "metadata": {},
      "source": [
        "<span id=\"52-steps-2-and-3-optimize-and-execute\" />\n",
        "\n",
        "### 5.2 Pasos 2 y 3: Optimizar y ejecutar\n",
        "\n",
        "Observamos que el mapa de acoplamiento del procesador Eagle tiene 144 aristas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f9fadf76-48e3-43a3-b773-8fa3b5fd5f3d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "156 qubits × 60 layers (221-depth),  Gate breakdown: SDG 7040, UNITARY 7040, CX 3520, R 3120\n",
            "156 qubits × 60 layers (201-depth),  Gate breakdown: RZ 11933, SX 6240, CZ 3520\n"
          ]
        }
      ],
      "source": [
        "# backend = service.backend(\"ibm_brisbane\")\n",
        "backend = backend_map\n",
        "\n",
        "transpiled_qc = transpile(qc, backend, optimization_level=1, layout_method=\"trivial\")\n",
        "transpiled_obs = obs.apply_layout(layout=transpiled_qc.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "2ddff1e1-c868-4300-bdbb-cdd3b6932302",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job id=d1479n6qf56g0081sxa0\n"
          ]
        }
      ],
      "source": [
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend)\n",
        "pub = (transpiled_qc, transpiled_obs, params)\n",
        "job = estimator.run([pub])\n",
        "\n",
        "job_id = job.job_id()\n",
        "print(f\"job id={job_id}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c497fc9d-aa58-4eb2-851e-369c7209da97",
      "metadata": {},
      "source": [
        "<span id=\"53-post-process\" />\n",
        "\n",
        "### 5.3 Postprocesamiento\n",
        "\n",
        "Proporcionamos los valores de los puntos \"mitigados\" en la figura 4b del documento de utilidad.   Represéntalos junto con tus resultados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "1c4ede0c-e865-41fe-9b38-8ecf39afa64f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/1c4ede0c-e865-41fe-9b38-8ecf39afa64f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "result_paper = [\n",
        "    1.0171,\n",
        "    1.0044,\n",
        "    0.9563,\n",
        "    0.9602,\n",
        "    0.8394,\n",
        "    0.8120,\n",
        "    0.5466,\n",
        "    0.4556,\n",
        "    0.1953,\n",
        "    0.0141,\n",
        "    0.0117,\n",
        "]\n",
        "\n",
        "# REPLACE WITH YOUR OWN JOB ID\n",
        "job = service.job(job_id)\n",
        "\n",
        "plt.plot(angles, job.result()[0].data.evs, \"o\", label=f\"{job.backend().name}\")\n",
        "plt.plot(angles, result_paper, \"o\", label=\"Utility Paper\")\n",
        "plt.xlabel(\"$\\\\mathrm{R_x}$ angle $\\\\theta_h$\")\n",
        "plt.title(\"$\\\\langle Z_{62} \\\\rangle$\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7fb3e103-af1d-4a9f-8016-1a270c5706be",
      "metadata": {},
      "source": [
        "¿Son sus resultados similares a los \"no mitigados\" de la figura 4b? Podrían ser muy diferentes, dependiendo del aparato y de su estado en el momento del experimento.  No se preocupe por los resultados en sí. Lo que comprobaremos es si ha realizado la codificación correctamente.  Si lo ha hecho, enhorabuena, ha llegado a la línea de salida de la era de los servicios públicos.\n",
        "\n",
        "Al igual que en el artículo de Utility, los científicos de todo el mundo han desplegado un enorme ingenio para extraer resultados significativos incluso en presencia de ruido. El objetivo final de este esfuerzo colectivo es la ventaja cuántica: un estado en el que los ordenadores cuánticos puedan resolver algunos problemas de utilidad en la industria más rápidamente, con mayor fidelidad o de forma más barata que los ordenadores clásicos. No es probable que se trate de un acontecimiento aislado, sino más bien de una era en la que la reproducción clásica de los resultados cuánticos tarda progresivamente más tiempo, hasta que en algún momento ese plazo cuántico adquiere una importancia crítica. Una cosa está clara sobre la ventaja cuántica: sólo llegaremos a ella a través de los experimentos a escala de utilidad.  Si este curso hace que te unas a la búsqueda, que está llena de retos y diversión, estaremos más que contentos.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a346d104-299e-4152-a8ea-5e297d05b6a1",
      "metadata": {},
      "source": [
        "<span id=\"reference\" />\n",
        "\n",
        "## Referencia\n",
        "\n",
        "* Kim, Y., Eddins, A., Anand, S. et al. Pruebas de la utilidad de la computación cuántica ante la tolerancia a fallos. Nature 618, 500-505 (2023). [https://doi.org/10.1038/s41586-023-06096-3](https://doi.org/10.1038/s41586-023-06096-3)\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"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}