{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d2c31ae8",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Corte de cables para la estimación de valores esperados\"\n",
        "description: \"Utilice el corte de cables para dividir los circuitos en muchos subcircuitos más pequeños.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore edgecolor Cutqc forall fontsize */}\n",
        "\n",
        "<span id=\"wire-cutting-for-expectation-values-estimation\" />\n",
        "\n",
        "# Corte de cables para la estimación de valores esperados\n",
        "\n",
        "*Tiempo estimado de ejecución: 22 segundos en un procesador Heron (NOTA: Se trata únicamente de una estimación). (El tiempo de ejecución puede variar.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados del aprendizaje\n",
        "\n",
        "Una vez completado este tutorial, los usuarios deberían comprender:\n",
        "\n",
        "* Cómo utilizar [`qiskit-addon-cutting`](https://github.com/Qiskit/qiskit-addon-cutting) para dividir un circuito grande en subcircuitos más pequeños, reduciendo así el efecto del ruido\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Requisitos previos\n",
        "\n",
        "Recomendamos a los usuarios que se familiaricen con el siguiente tema antes de seguir este tutorial:\n",
        "\n",
        "* Utilizando la primitiva [«Sampler»](/docs/api/qiskit-ibm-runtime/sampler-v2), que se emplea en este flujo de trabajo\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "El «tejido de circuitos» es un término genérico que engloba diversos métodos para dividir un circuito en varios subcircuitos más pequeños que contienen un número menor de puertas lógicas o qubits. Cada uno de los subcircuitos puede ejecutarse de forma independiente, y el resultado final se obtiene mediante un procesamiento posterior clásico del resultado de cada subcircuito. Esta técnica está disponible en el [complemento «Circuit cutting» de Qiskit](https://qiskit.github.io/qiskit-addon-cutting/index.html); consulta la [documentación](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) y otros [materiales introductorios](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) para obtener una explicación detallada de la técnica.\n",
        "\n",
        "Este tutorial se centra en un método denominado **«corte de cable»**, en el que el circuito se divide a lo largo del cable [\\[1\\], \\[2\\]](#references). Cabe señalar que la partición es sencilla en los circuitos clásicos, ya que el resultado en el punto de partición puede determinarse de forma determinista y es 0 o 1. Sin embargo, el estado del qubit en el momento del corte es, en general, un estado mixto. Por lo tanto, cada subcircuito debe medirse varias veces en diferentes bases (normalmente una base tomográficamente completa, como la base de Pauli [\\[3\\], \\[4\\]](#references) ) y prepararse en consecuencia en su estado propio. La siguiente figura (fuente: [\\[7\\]](#references) ) muestra un ejemplo de división de un estado GHZ de cuatro qubits en tres subcircuitos. Aquí, $M_j$ denota un conjunto de bases (normalmente Pauli X, Y y Z), y $P_i$ denota un conjunto de estados propios (normalmente $|0\\rangle$, $|1\\rangle$, $|+\\rangle$ y $|+i\\rangle$ ).\n",
        "\n",
        "![wc-1.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/0ce8857b-7f5f-400e-8536-6a496c724d50.avif)\n",
        "![wc-2.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/cbce4455-4794-4c81-8630-3e3993e1b29f.avif)\n",
        "\n",
        "Dado que cada subcircuito tiene menos qubits y puertas lógicas, se espera que sean menos sensibles al ruido. Este tutorial muestra un ejemplo en el que se puede utilizar este método para suprimir eficazmente el ruido del sistema.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de empezar 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.22 o posterior ( `pip install qiskit-ibm-runtime` )\n",
        "* Complemento de diseño de circuitos de Qiskit v0.10.0 o posterior (`pip install qiskit-addon-cutting`)\n",
        "* Utilidades del complemento Qiskit: 0.3 o posterior (`pip install qiskit-addon-utils`)\n",
        "* Qiskit Aer (`pip install qiskit-aer` )\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
        "from qiskit.quantum_info import PauliList, SparsePauliOp\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit.result import sampled_expectation_value\n",
        "\n",
        "from qiskit_addon_cutting.instructions import CutWire\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_wires,\n",
        "    expand_observables,\n",
        "    partition_problem,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "431a5bd2-e6ed-471b-ad9e-c4edd27784a8",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Ejemplo de simulador a pequeña escala\n",
        "\n",
        "Este tutorial implementa un [patrón de Qiskit](/docs/guides/intro-to-patterns) para simular un circuito de localización de muchos cuerpos (MBL) unidimensional ( 1D ). El circuito MBL es un circuito eficiente en cuanto a hardware y se parametriza mediante dos parámetros: $\\theta$ y $\\vec{\\phi}$. Cuando $\\theta$ se establece en $0$ y se prepara el estado inicial en $|0\\rangle$ para todos los qubits, el valor esperado ideal de $\\langle Z_i \\rangle$ es $+1$ para cada sitio de qubit $i$, independientemente de los valores de $\\vec{\\phi}$. En este [artículo](https://www.nature.com/articles/s41467-025-57623-x) se ofrece más información sobre este circuito.\n",
        "\n",
        "Ten en cuenta que, en un simulador sin ruido, el valor esperado obtenido con y sin corte del circuito será el mismo.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Paso 1: Asignar entradas clásicas a un problema cuántico\n",
        "\n",
        "<span id=\"construct-the-1d-mbl-circuit\" />\n",
        "\n",
        "#### Construye el circuito MBL « 1D »\n",
        "\n",
        "En primer lugar, presentamos una función para construir el circuito MBL « 1D ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "d2aa988a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class MBLChainCircuit(QuantumCircuit):\n",
        "    def __init__(\n",
        "        self, num_qubits: int, depth: int, use_cut: bool = False\n",
        "    ) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainCircuit<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "        evolution = MBLChainEvolution(num_qubits, depth, use_cut)\n",
        "        self.compose(evolution, inplace=True)\n",
        "\n",
        "\n",
        "class MBLChainEvolution(QuantumCircuit):\n",
        "    def __init__(self, num_qubits: int, depth: int, use_cut) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainEvolution<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "\n",
        "        theta = Parameter(\"θ\")\n",
        "        phis = ParameterVector(\"φ\", num_qubits)\n",
        "\n",
        "        for layer in range(depth):\n",
        "            layer_parity = layer % 2\n",
        "            # print(\"layer parity\", layer_parity)\n",
        "            for qubit in range(layer_parity, num_qubits - 1, 2):\n",
        "                # print(qubit)\n",
        "                self.cz(qubit, qubit + 1)\n",
        "                self.u(theta, 0, np.pi, qubit)\n",
        "                self.u(theta, 0, np.pi, qubit + 1)\n",
        "                if (\n",
        "                    use_cut\n",
        "                    and layer_parity == 0\n",
        "                    and (\n",
        "                        qubit == num_qubits // 2 - 1\n",
        "                        or qubit == num_qubits // 2\n",
        "                    )\n",
        "                ):\n",
        "                    self.append(CutWire(), [num_qubits // 2])\n",
        "                if use_cut and layer < depth - 1 and layer_parity == 1:\n",
        "                    if qubit == num_qubits // 2:\n",
        "                        self.append(CutWire(), [qubit])\n",
        "            for qubit in range(num_qubits):\n",
        "                self.p(phis[qubit], qubit)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a3debf65-06df-4277-933e-14b6f6170756",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/a3debf65-06df-4277-933e-14b6f6170756-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_qubits = 10\n",
        "depth = 2\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "mbl.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ef13f367",
      "metadata": {},
      "source": [
        "Calculamos el valor esperado medio $O = \\frac{1}{n} \\sum_i Z_i$ para todos los qubits en $\\theta = 0$. Dado que el valor esperado ideal de $\\langle Z_i \\rangle = 1$ $\\forall$ $i$, el valor esperado ideal de $O$ es también $1$. Los parámetros $\\phi$ se seleccionan al azar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b0f1e5fd",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a81f0346",
      "metadata": {},
      "source": [
        "Para dividir el circuito, hay que anotarlo insertando « **CutWire** » en los puntos deseados. Para este tutorial, optamos por una partición equitativa. El circuito MBL está diseñado de tal manera que, al establecer `use_cut=True` en la función, la anotación se inserta correctamente después de $\\frac{n}{2}$ qubits, donde $n$ es el número de qubits del circuito original. También asignamos los parámetros generados aleatoriamente al circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "5208e0a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/5208e0a8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_cut.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Paso 2: Optimizar el problema para la ejecución en hardware cuántico\n",
        "\n",
        "<span id=\"cut-the-circuit-into-smaller-subcircuits\" />\n",
        "\n",
        "#### Divida el circuito en subcircuitos más pequeños\n",
        "\n",
        "Ahora dividimos el circuito en dos subcircuitos más pequeños utilizando [`qiskit-addon-cutting`](https://qiskit.github.io/qiskit-addon-cutting/). `qiskit-addon-cutting` añade una puerta virtual `Move` para dividir el punto de corte del cable ajustando adecuadamente el número de qubits. Ahora creamos el circuito con esta puerta virtual. Dado que hay un cable cortado, el número de qubits asociados aumentará en 1.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "1834cb22",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1834cb22-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_move = cut_wires(mbl_cut)\n",
        "mbl_move.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1df16e6b",
      "metadata": {},
      "source": [
        "<span id=\"construct-and-expand-the-observable\" />\n",
        "\n",
        "#### Construir y ampliar el observable\n",
        "\n",
        "La observable, tal y como se ha definido anteriormente, será la media de l $Z$ e en cada qubit. Sin embargo, al insertar la puerta virtual `Move` , el número efectivo de qubits del circuito aumenta. El observable también debe ampliarse en consecuencia para tener en cuenta este cambio en el número de qubits. Ten en cuenta que el observable siempre actúa de forma trivial (tal y como se describe en $I$ ) sobre el qubit adicional añadido para la puerta virtual `Move` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3074b173",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',\n",
              "           'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',\n",
              "           'IIIIIIIIZI', 'IIIIIIIIIZ'])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "observable"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32b7081b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',\n",
              "           'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',\n",
              "           'IIIIIIIIIZI', 'IIIIIIIIIIZ'])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "new_obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad0886b",
      "metadata": {},
      "source": [
        "Ahora el circuito se puede dividir a lo largo de `Move` la puerta y obtenemos los subcircuitos, así como el subobservable, que es la parte del observable original asociada a cada subcircuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c8894a06",
      "metadata": {},
      "outputs": [],
      "source": [
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f47ab33",
      "metadata": {},
      "source": [
        "Aquí se muestran los dos subcircuitos:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1b0e779f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1b0e779f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "3c802f28",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/3c802f28-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[1].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e4c03fb",
      "metadata": {},
      "source": [
        "Para ampliar el observable mediante la `Move` operación se necesita una `PauliList` estructura de datos. Para reconstruir el valor esperado del circuito original, necesitamos que la observable tenga el `SparsePauliOp` formato.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "7dc51220",
      "metadata": {},
      "outputs": [],
      "source": [
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b5a5746",
      "metadata": {},
      "source": [
        "Como se ha comentado anteriormente, para cada corte, el circuito de entrada debe medirse en una base de Pauli, y el circuito de salida debe prepararse en el estado propio de dicha base. La función `generate_cutting_experiments` crea todos los circuitos necesarios y los coeficientes asociados a cada uno de ellos que se requieren para la reconstrucción. Encontrarás más detalles en [este artículo](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.125.150504).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "648519f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43f58cfb",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuits-onto-the-backend\" />\n",
        "\n",
        "#### Compilar los circuitos en el backend\n",
        "\n",
        "En el primer ejemplo, que solo implica simulación, compilamos el circuito al conjunto de puertas básicas del backend:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "29d71cd3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=133\n",
        ")\n",
        "\n",
        "print(backend)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Paso 3: Ejecutar utilizando Qiskit primitives\n",
        "\n",
        "Ahora, ejecuta cada subexperimento:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "05fabb27",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=2, basis_gates=backend.configuration().basis_gates\n",
        ")\n",
        "basis_subexperiments = {\n",
        "    label: pm_basis.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b22a1b00",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=AerSimulator())\n",
        "jobs = {\n",
        "    label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "    for label, subsystem_subexpts in basis_subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Paso 4: Procesamiento posterior y devolución del resultado en el formato clásico deseado\n",
        "\n",
        "Ahora recuperamos el resultado de cada ejecución del subexperimento y reconstruimos el valor esperado del circuito sin recortar:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "31dc35ea-6554-4ca7-9c3b-0b5394c46e4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results\n",
        "results = {label: job.result() for label, job in jobs.items()}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "4972e4e4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.float64(0.9953821063041687)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "reconstructed_expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "fb5f955a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, '$M_Z$')"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/fb5f955a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "methods = [\n",
        "    \"Uncut\",\n",
        "    \"Wire cut\",\n",
        "]\n",
        "values = [\n",
        "    1,\n",
        "    reconstructed_expval,\n",
        "]  # since the ideal expectation value in noiseless simulation is +1\n",
        "\n",
        "ax = plt.gca()\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "ax.set_ylabel(r\"$M_Z$\", fontsize=12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d6db390-e7a8-4efe-902c-8d9a312170c6",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Ejemplo de hardware a gran escala\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae69c5e0-32b1-4f03-ab13-7b95a9acfd25",
      "metadata": {},
      "source": [
        "A continuación, mostramos el proceso de corte de cables para un circuito MBL de 60 qubits. Tanto los circuitos sin cortar como los cortados se ejecutarán en un hardware de tip IBM Quantum® :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "37834c72",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/37834c72-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "num_qubits = 60\n",
        "depth = 2\n",
        "\n",
        "# construct the circuit\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "\n",
        "# create parameters\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis\n",
        "\n",
        "# construct the cut circuit\n",
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_move = cut_wires(mbl_cut)\n",
        "\n",
        "# Define observable and expand to account for the wire cut\n",
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "\n",
        "# Construct a SparsePauliOp version of the observable for later use in reconstruction\n",
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")\n",
        "\n",
        "# Partition the circuit and get subcircuits and subobservables\n",
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables\n",
        "\n",
        "# Obtain subexperiments and coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")\n",
        "\n",
        "# Transpile the subexperiments to the backend\n",
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "isa_subexperiments = {\n",
        "    label: pm.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}\n",
        "\n",
        "# Execute the subexperiments and retrieve results\n",
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(mode=batch)\n",
        "    sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "    jobs = {\n",
        "        label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "        for label, subsystem_subexpts in isa_subexperiments.items()\n",
        "    }\n",
        "results = {label: job.result() for label, job in jobs.items()}\n",
        "\n",
        "# Reconstruct the expectation value of the original observable\n",
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "\n",
        "# Compute the uncut circuit to obtain the noisy expectation value for comparison\n",
        "sampler = SamplerV2(mode=backend)\n",
        "sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "\n",
        "if mbl.num_clbits == 0:\n",
        "    mbl.measure_all()\n",
        "isa_mbl = pm.run(mbl)\n",
        "\n",
        "pub = (isa_mbl, params)\n",
        "uncut_job = sampler.run([pub])\n",
        "\n",
        "uncut_counts = uncut_job.result()[0].data.meas.get_counts()\n",
        "uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
        "\n",
        "# visualize the results\n",
        "ax = plt.gca()\n",
        "methods = [\"uncut\", \"cut\"]\n",
        "values = [uncut_expval, reconstructed_expval]\n",
        "\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "plt.axhline(y=1, color=\"k\", linestyle=\"--\")\n",
        "plt.text(0.3, 0.95, \"Exact result\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "1445d099",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.9202473958333336"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uncut_expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75f48e6a-c7e4-46f3-9d39-a7a877427a04",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  Si te ha parecido interesante este trabajo, quizá te interese el siguiente material:\n",
        "\n",
        "  * [Condiciones de contorno periódicas con corte de circuito](/docs/tutorials/periodic-boundary-conditions-with-circuit-cutting)\n",
        "  * [Corte en circuito para reducir la profundidad](/docs/tutorials/depth-reduction-with-circuit-cutting)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23cd3042",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referencias\n",
        "\n",
        "\\[1] Peng, T., Harrow, A. W., Ozols, M., y Wu, X. (2020). Simulación de grandes circuitos cuánticos en un pequeño ordenador cuántico. Physical review letters, 125(15), 150504.\n",
        "\n",
        "\\[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., y Martonosi, M. (2021, abril). Cutqc: uso de pequeños ordenadores cuánticos para grandes evaluaciones de circuitos cuánticos. En Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).\n",
        "\n",
        "\\[3] Perlin, M. A., Saleem, Z. H., Suchara, M., y Osborn, J. C. (2021). Corte de circuitos cuánticos con tomografía de máxima verosimilitud. npj Quantum Information, 7(1), 64.\n",
        "\n",
        "\\[4] Majumdar, R., y Wood, C. J. (2022). Corte de circuitos cuánticos con mitigación de errores. arXiv preimpresión arXiv:2211.13431.\n",
        "\n",
        "\\[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Paralelización de cargas de trabajo cuántico-clásicas: Perfiles del impacto de las técnicas de división. En 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.\n",
        "\n",
        "\\[6] Bhoumik, D., Majumdar, R., Saha, A., y Sur-Kolay, S. (2023). Programación distribuida de circuitos cuánticos con ruido y optimización del tiempo. arXiv preimpresión arXiv:2309.06005.\n",
        "\n",
        "\\[7] Majumdar, R. (2024). Reducción eficiente del consumo de recursos y del ruido en circuitos de computación cuántica discreta (Tesis doctoral, Instituto Indio de Estadística - Calcuta). [https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y](https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y)\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.5,
    "qpuSeconds": 22
  },
  "nbformat": 4,
  "nbformat_minor": 5
}