{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb16d89d-f6fc-417f-9ce5-19251d039df7",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Corte de circuitos para condiciones de contorno periódicas\"\n",
        "description: \"Utilice el corte de circuitos para resolver un problema de cadena periódica a escala de servicios públicos en el que el primer y el último qubit no son vecinos.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize edgecolor */}\n",
        "\n",
        "<span id=\"circuit-cutting-for-periodic-boundary-conditions\" />\n",
        "\n",
        "# Corte de circuitos para condiciones de contorno periódicas\n",
        "\n",
        "*Estimación de uso: Dos minutos en un procesador Eagle (NOTA: Esto es sólo una estimación. Su tiempo de ejecución puede variar)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f053a0b1",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "En este cuaderno, consideramos la simulación de una cadena periódica de qubits donde hay una operación de dos qubits entre cada dos qubits adyacentes, incluyendo el primero y el último. Las cadenas periódicas se encuentran a menudo en problemas de física y química, como los modelos Ising y la simulación molecular.\n",
        "\n",
        "Los dispositivos actuales IBM Quantum® son planares. Es posible incrustar algunas cadenas periódicas en la topología directamente donde el primer y el último qubits son vecinos. Sin embargo, para problemas suficientemente grandes, el primer y el último qubits pueden estar muy separados, lo que requiere muchas puertas SWAP para la operación de 2 qubits entre estos dos qubits. Este problema de frontera periódica se ha estudiado en <a href=\"https://arxiv.org/abs/2402.17833\">este documento</a>.\n",
        "\n",
        "En este cuaderno mostramos el uso del corte de circuitos para tratar un problema de cadena periódica a escala de utilidad de este tipo en el que el primer y el último qubits no son vecinos. Cortar esta conectividad de largo alcance evita las puertas SWAP extra a costa de ejecutar múltiples instancias del circuito, y algún post-procesamiento clásico. En resumen, se puede incorporar el corte para calcular lógicamente las operaciones de 2 qubits de larga distancia. En otras palabras, este enfoque conduce a un aumento efectivo de la conectividad del mapa de acoplamiento, lo que conduce a un menor número de puertas SWAP.\n",
        "\n",
        "Obsérvese que hay dos tipos de cortes: cortar el cable de un circuito (llamado `wire cutting`), o sustituir una puerta de 2 qubits por múltiples operaciones de un solo qubit (llamado `gate cutting`). En este cuaderno, nos centraremos en el corte de compuertas. Para más detalles sobre el corte de compuertas, consulte <a href=\"https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html\">material explicativo</a> en `qiskit-addon-cutting`, y las referencias correspondientes. Para obtener más información sobre el corte de cables, consulte el tutorial [Corte de cables para la estimación de valores de expectativa](/docs/tutorials/wire-cutting) o los tutoriales de <a href=\"https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html\">qiskit-addon-corte</a>.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "120312e5-0eed-4168-9098-633a3d0e6e57",
      "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 v1.2 o posterior (`pip install qiskit`)\n",
        "* Qiskit Runtime v0.3 o posterior (`pip install qiskit-ibm-runtime`)\n",
        "* Complemento Qiskit para corte de circuitos v.9.0 o posterior (`pip install qiskit-addon-cutting`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f0af3c7d",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f01e3062",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib as mpl\n",
        "\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.passes import (\n",
        "    BasisTranslator,\n",
        "    Optimize1qGatesDecomposition,\n",
        ")\n",
        "from qiskit.circuit.equivalence_library import (\n",
        "    SessionEquivalenceLibrary as sel,\n",
        ")\n",
        "from qiskit.converters import circuit_to_dag, dag_to_circuit\n",
        "from qiskit.result import sampled_expectation_value\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.circuit.library import TwoLocal\n",
        "\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_gates,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, SamplerOptions, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6e7c685c-3e10-4cf1-a435-2b0fc761ebc4",
      "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",
        "Aquí generaremos un circuito TwoLocal y definiremos algunos observables.\n",
        "\n",
        "<ul>\n",
        "  <li>Entrada: Parámetros para crear un circuito</li>\n",
        "  <li>Salida: Circuito abstracto y observables</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8590c112-cf8a-4bc3-ab8b-411d1a1b010a",
      "metadata": {},
      "source": [
        "Consideramos un hardware-eficiente `entangler map` para el circuito TwoLocal con conectividad periódica entre el último y el primer qubits del `entangler map`. Esta interacción de largo alcance puede dar lugar a puertas SWAP adicionales durante la transpilación, aumentando así la profundidad del circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0776185d-4ea8-4a8c-ab14-121636444d8f",
      "metadata": {},
      "source": [
        "<span id=\"select-backend-and-initial-layout\" />\n",
        "\n",
        "#### Seleccionar backend y diseño inicial\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "81c616a7-248a-412b-bac5-080eb9199760",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15b9c4e5-7b25-4b95-9d79-bf73b9adad97",
      "metadata": {},
      "source": [
        "Para este cuaderno consideraremos una cadena 1D periódica de 109 qubits, que es la cadena 1D más larga en la topología de un dispositivo cuántico IBM de 127 qubits. No es posible disponer una cadena periódica de 109 qubits en un dispositivo de 127 qubits de forma que el primer y el último qubits sean vecinos sin incorporar puertas SWAP adicionales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "6f8c4588-0532-41f9-9d6d-50e754466593",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "109"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "init_layout = [\n",
        "    13,\n",
        "    12,\n",
        "    11,\n",
        "    10,\n",
        "    9,\n",
        "    8,\n",
        "    7,\n",
        "    6,\n",
        "    5,\n",
        "    4,\n",
        "    3,\n",
        "    2,\n",
        "    1,\n",
        "    0,\n",
        "    14,\n",
        "    18,\n",
        "    19,\n",
        "    20,\n",
        "    21,\n",
        "    22,\n",
        "    23,\n",
        "    24,\n",
        "    25,\n",
        "    26,\n",
        "    27,\n",
        "    28,\n",
        "    29,\n",
        "    30,\n",
        "    31,\n",
        "    32,\n",
        "    36,\n",
        "    51,\n",
        "    50,\n",
        "    49,\n",
        "    48,\n",
        "    47,\n",
        "    46,\n",
        "    45,\n",
        "    44,\n",
        "    43,\n",
        "    42,\n",
        "    41,\n",
        "    40,\n",
        "    39,\n",
        "    38,\n",
        "    37,\n",
        "    52,\n",
        "    56,\n",
        "    57,\n",
        "    58,\n",
        "    59,\n",
        "    60,\n",
        "    61,\n",
        "    62,\n",
        "    63,\n",
        "    64,\n",
        "    65,\n",
        "    66,\n",
        "    67,\n",
        "    68,\n",
        "    69,\n",
        "    70,\n",
        "    74,\n",
        "    89,\n",
        "    88,\n",
        "    87,\n",
        "    86,\n",
        "    85,\n",
        "    84,\n",
        "    83,\n",
        "    82,\n",
        "    81,\n",
        "    80,\n",
        "    79,\n",
        "    78,\n",
        "    77,\n",
        "    76,\n",
        "    75,\n",
        "    90,\n",
        "    94,\n",
        "    95,\n",
        "    96,\n",
        "    97,\n",
        "    98,\n",
        "    99,\n",
        "    100,\n",
        "    101,\n",
        "    102,\n",
        "    103,\n",
        "    104,\n",
        "    105,\n",
        "    106,\n",
        "    107,\n",
        "    108,\n",
        "    112,\n",
        "    126,\n",
        "    125,\n",
        "    124,\n",
        "    123,\n",
        "    122,\n",
        "    121,\n",
        "    120,\n",
        "    119,\n",
        "    118,\n",
        "    117,\n",
        "    116,\n",
        "    115,\n",
        "    114,\n",
        "    113,\n",
        "]\n",
        "\n",
        "# the number of qubits in the circuit is governed by the length of the initial layout\n",
        "num_qubits = len(init_layout)\n",
        "num_qubits"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da94a31f-3bce-40e5-a580-b938ff255425",
      "metadata": {},
      "source": [
        "<span id=\"build-the-entangler-map-for-the-twolocal-circuit\" />\n",
        "\n",
        "#### Construye el mapa del entrelazador para el circuito TwoLocal\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "15bc3ac1-dbb6-4bb5-8d3e-63f3638f7a96",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = [(i, i + 1) for i in range(0, len(init_layout) - 1)]\n",
        "coupling_map.append(\n",
        "    (len(init_layout) - 1, 0)\n",
        ")  # adding in the periodic connectivity"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7b4970f-ac13-4644-a92f-1796de876767",
      "metadata": {},
      "source": [
        "TwoLocal el circuito permite repetir varias veces el `rotation_blocks` y el `entangler map` . En este caso, el número de repeticiones determina el número de puertas periódicas que hay que cortar. Dado que la sobrecarga de muestreo aumenta exponencialmente con el número de cortes (para más detalles, consulte el tutorial [Wire cutting for expectation values estimation](/docs/tutorials/wire-cutting) ), fijaremos el número de repeticiones en 2 en este cuaderno.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "0cd786ff-9798-4e20-937f-a258eea88077",
      "metadata": {},
      "outputs": [],
      "source": [
        "num_reps = 2\n",
        "entangler_map = []\n",
        "\n",
        "for even_edge in coupling_map[0 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(even_edge)\n",
        "\n",
        "for odd_edge in coupling_map[1 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(odd_edge)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "79428537-66cf-40ce-87cf-0f75f591cb4b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/79428537-66cf-40ce-87cf-0f75f591cb4b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ansatz = TwoLocal(\n",
        "    num_qubits=num_qubits,\n",
        "    rotation_blocks=\"rx\",\n",
        "    entanglement_blocks=\"cx\",\n",
        "    entanglement=entangler_map,\n",
        "    reps=num_reps,\n",
        ").decompose()\n",
        "ansatz.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "18b2f9b3-07f0-47d0-88db-7182e03c5f53",
      "metadata": {},
      "source": [
        "Para verificar la calidad del resultado mediante el corte de circuitos, necesitamos conocer el resultado ideal. El circuito elegido actualmente va más allá de la simulación clásica por fuerza bruta. Por lo tanto, fijamos cuidadosamente los parámetros del circuito para que sea clifford.\n",
        "\n",
        "Asignaremos el valor de parámetro $0$ para las dos primeras capas de compuertas `Rx` , y el valor $\\pi$ para la última capa. Esto garantiza que el resultado ideal de este circuito es $|1\\rangle^{\\otimes n}$, siendo $n$ el número de qubits. Por lo tanto, los valores de expectativa de $\\langle Z_i \\rangle$ y $\\langle Z_i Z_{i+1} \\rangle$, donde $i$ es el índice del qubit, son $-1$ y $+1$ respectivamente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a0d70827-ae61-49a0-b14c-c10b27963262",
      "metadata": {},
      "outputs": [],
      "source": [
        "params_last_layer = [np.pi] * ansatz.num_qubits\n",
        "params = [0] * (ansatz.num_parameters - ansatz.num_qubits)\n",
        "params.extend(params_last_layer)\n",
        "\n",
        "ansatz.assign_parameters(params, inplace=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f4289dd-30fd-4295-9d33-488f2fc03a3a",
      "metadata": {},
      "source": [
        "<span id=\"select-observables\" />\n",
        "\n",
        "#### Seleccionar observables\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a359888-c685-4b28-b2d4-c29b89448e3b",
      "metadata": {},
      "source": [
        "Para cuantificar las ventajas del corte de puertas, medimos los valores de expectativa de los observables $\\frac{1}{n}\\sum_{i=1}^n \\langle Z_i \\rangle$ y $\\frac{1}{n-1}\\sum_{i=1}^{n-1} \\langle Z_i Z_{i+1} \\rangle$. Como ya se ha dicho, los valores de expectativa ideales son $-1$ y $+1$ respectivamente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "87a6a367-2d4e-410c-8e5f-3ef069f968d8",
      "metadata": {},
      "outputs": [],
      "source": [
        "observables = []\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    obs = \"I\" * (i) + \"Z\" + \"I\" * (num_qubits - i - 1)\n",
        "    observables.append(obs)\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    if i == num_qubits - 1:\n",
        "        obs = \"Z\" + \"I\" * (num_qubits - 2) + \"Z\"\n",
        "    else:\n",
        "        obs = \"I\" * i + \"ZZ\" + \"I\" * (num_qubits - i - 2)\n",
        "    observables.append(obs)\n",
        "\n",
        "observables = SparsePauliOp(observables)\n",
        "paulis = observables.paulis\n",
        "coeffs = observables.coeffs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "906440c1-4e5b-4f18-9bc7-d450dd7d2b24",
      "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",
        "<ul>\n",
        "  <li>Entrada: Circuito abstracto y observables</li>\n",
        "  <li>Salida: Circuito objetivo y observables producidos por el corte de puertas de largo alcance</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2a7387e5-ead4-4dd8-a01f-974138cda3d9",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuit\" />\n",
        "\n",
        "#### Transpilar el circuito\n",
        "\n",
        "Tenga en cuenta que el circuito se puede transpilar en esta fase o después de cortarlo. Si transpilamos después del corte, tendremos que transpilar cada uno de los subexperimentos generados debido a la sobrecarga de muestreo. Por lo tanto, es más prudente transpilar en esta fase para reducir la sobrecarga de la transpilación.\n",
        "\n",
        "Sin embargo, si la transpilación se realiza en esta etapa con conectividad de hardware nativa, el transpilador añadirá múltiples puertas SWAP para colocar la operación periódica de 2 qubits - ofuscando los beneficios del recorte de circuitos. Para evitar este problema podemos aprovechar que conocemos las puertas exactas que hay que cortar. En concreto, podemos crear un mapa de acoplamiento virtual añadiendo conexiones virtuales entre qubits lejanos para acomodar estas puertas periódicas de 2 qubits. Esto garantizará que el circuito pueda transpilarse en esta fase sin incorporar las puertas SWAP adicionales.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c12bb07e-54a0-4597-8b4d-4d0fbf6a4c99",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = backend.configuration().coupling_map\n",
        "\n",
        "# create a virtual coupling map with long range connectivity\n",
        "virtual_coupling_map = coupling_map.copy()\n",
        "virtual_coupling_map.append([init_layout[-1], init_layout[0]])\n",
        "virtual_coupling_map.append([init_layout[0], init_layout[-1]])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ad38aa32-4613-46c5-bf62-da332a1b9dfb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/ad38aa32-4613-46c5-bf62-da332a1b9dfb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm_virtual = generate_preset_pass_manager(\n",
        "    optimization_level=1,\n",
        "    coupling_map=virtual_coupling_map,\n",
        "    initial_layout=init_layout,\n",
        "    basis_gates=backend.configuration().basis_gates,\n",
        ")\n",
        "\n",
        "virtual_mapped_circuit = pm_virtual.run(ansatz)\n",
        "virtual_mapped_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ecb4ae6e-f9a5-4453-a8a3-1a5aaaf954da",
      "metadata": {},
      "source": [
        "<span id=\"cut-the-long-range-periodic-connectivities\" />\n",
        "\n",
        "#### Cortar las conectividades periódicas de largo alcance\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c155eda9-ba08-4069-9962-ece21926f1db",
      "metadata": {},
      "source": [
        "Ahora cortamos las puertas del circuito transpilado. Obsérvese que las puertas de 2 qubits que hay que cortar son las que conectan el último y el primer qubits del esquema.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "718ea31e-c8d8-4cf9-975b-fc0e77fb27c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Find the indices of the distant gates\n",
        "cut_indices = [\n",
        "    i\n",
        "    for i, instruction in enumerate(virtual_mapped_circuit.data)\n",
        "    if {virtual_mapped_circuit.find_bit(q)[0] for q in instruction.qubits}\n",
        "    == {init_layout[-1], init_layout[0]}\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6091cb0-227d-454a-a2d8-7ba685b66121",
      "metadata": {},
      "source": [
        "Aplicaremos el trazado del circuito transpilado al observable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "af57d942-5997-4920-91c6-295fbfef478d",
      "metadata": {},
      "outputs": [],
      "source": [
        "trans_observables = observables.apply_layout(virtual_mapped_circuit.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a9a20e4-b722-456d-885b-d68ead3d341e",
      "metadata": {},
      "source": [
        "Por último, los subexperimentos se generan por muestreo sobre diferentes bases de medición y preparación.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "981f111c-69e6-44c2-a297-c7df302cbc0e",
      "metadata": {},
      "outputs": [],
      "source": [
        "qpd_circuit, bases = cut_gates(virtual_mapped_circuit, cut_indices)\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=qpd_circuit,\n",
        "    observables=trans_observables.paulis,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "60582298-52ae-4f10-b8c2-bc754cb6d24c",
      "metadata": {},
      "source": [
        "Obsérvese que el recorte de las interacciones de largo alcance conduce a la ejecución de múltiples muestras del circuito que difieren en las bases de medición y preparación. Encontrará más información al respecto en <a href=\"https://arxiv.org/abs/1909.07534\">Construcción de una puerta virtual de dos qubits mediante el muestreo de operaciones de un solo qubit</a> y <a href=\"https://arxiv.org/abs/2312.11638\">Circuitos de corte con múltiples unitarios de dos qubits</a>.\n",
        "\n",
        "El número de puertas periódicas a cortar es igual al número de repeticiones de la capa `TwoLocal` , definida como `num_reps` más arriba. La sobrecarga de muestreo del corte de la puerta es de 6. Por lo tanto, el número total de subexperimentos será $6^{num\\_reps}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "be4a43b4-c035-4814-a486-45eb9fe23d86",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Number of subexperiments is 36 = 6**2\n"
          ]
        }
      ],
      "source": [
        "print(f\"Number of subexperiments is {len(subexperiments)} = 6**{num_reps}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41a11eed-a8c0-4cce-b59d-ca49c54b52b1",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-subexperiments\" />\n",
        "\n",
        "#### Transpilar los subexperimentos\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5772d662-c030-4de6-9be4-e90cd599b173",
      "metadata": {},
      "source": [
        "En este punto, los subexperimentos contienen circuitos con algunas puertas de 1-qubit que no están en el conjunto de puertas base. Esto se debe a que los qubits cortados se miden en bases diferentes, y las puertas de rotación utilizadas para ello no pertenecen necesariamente al conjunto de puertas de base. Por ejemplo, la medición en base X implica aplicar una puerta Hadamard antes de la medición habitual en base Z. Pero Hadamard no forma parte del conjunto de puertas base.\n",
        "\n",
        "En lugar de aplicar todo el proceso de transpilación en cada uno de los circuitos de los subexperimentos, podemos utilizar pases de transpilación específicos. Consulte <a href=\"/docs/api/qiskit/transpiler_passes\">esta documentación</a> para obtener una descripción detallada de todos los pases de transpilación disponibles.\n",
        "\n",
        "Aplicaremos `BasisTranslator` y luego `Optimize1qGatesDecomposition` pases para garantizar que todas las puertas de estos circuitos pertenecen al conjunto de puertas base. Utilizar estas dos pasadas es más rápido que todo el proceso de transpilación, ya que otros pasos como el enrutamiento y la selección de la disposición inicial no se vuelven a realizar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9c6d074f-5e48-4adb-9f4f-c5b03c9f7e36",
      "metadata": {},
      "outputs": [],
      "source": [
        "pass_ = PassManager(\n",
        "    [Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates)]\n",
        ")\n",
        "\n",
        "subexperiments = pass_.run(\n",
        "    [\n",
        "        dag_to_circuit(\n",
        "            BasisTranslator(sel, target_basis=backend.basis_gates).run(\n",
        "                circuit_to_dag(circ)\n",
        "            )\n",
        "        )\n",
        "        for circ in subexperiments\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c5c0c60-3caa-4bd6-80aa-f7dc412680bb",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Paso 3: Ejecutar utilizando Qiskit primitives\n",
        "\n",
        "<ul>\n",
        "  <li>Entrada: Circuitos objetivo</li>\n",
        "  <li>Resultados: Distribuciones de cuasiprobabilidad</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1fb3b96c-e55c-40e4-a5ca-4c327a192d73",
      "metadata": {},
      "source": [
        "Utilizamos una primitiva `SamplerV2` para la ejecución de los circuitos de corte. Desactivamos `dynamical decoupling` y `twirling` para que cualquier mejora que obtengamos en el resultado se deba únicamente a la aplicación efectiva del corte de puertas para este tipo de circuitos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "69a7cc63-173b-467c-87c5-f0924b943f34",
      "metadata": {},
      "outputs": [],
      "source": [
        "options = SamplerOptions()\n",
        "options.default_shots = 10000\n",
        "options.dynamical_decoupling.enable = False\n",
        "options.twirling.enable_gates = False\n",
        "options.twirling.enable_measure = False"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9311a78-cdc7-4ce2-9716-a0cb5fc03589",
      "metadata": {},
      "source": [
        "Ahora enviaremos los trabajos utilizando el modo por lotes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6c1ba1d4-b4ed-4781-99f5-41e3f93672d7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID cwxf7wq60bqg008pvt8g\n"
          ]
        }
      ],
      "source": [
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(options=options)\n",
        "    cut_job = sampler.run(subexperiments)\n",
        "\n",
        "print(f\"Job ID {cut_job.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "54ca98b9-7f31-45d2-910a-97ad29b37a0d",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = cut_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dd7cbb19-22ab-4e5a-bd18-3c7b06f67478",
      "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",
        "<ul>\n",
        "  <li>Entrada: Distribuciones de cuasiprobabilidad</li>\n",
        "  <li>Resultados: Valores de expectativa reconstruidos</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3ee4af4c-4585-4a8d-87a6-921c2cdb1bd2",
      "metadata": {},
      "outputs": [],
      "source": [
        "reconstructed_expvals = reconstruct_expectation_values(\n",
        "    result,\n",
        "    coefficients,\n",
        "    paulis,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f2d78c56-001c-4fd5-97eb-6d45b0f84bda",
      "metadata": {},
      "source": [
        "Ahora calculamos la media de los observables de tipo Z de weight-1 y weight-2.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "00714269-8c72-47eb-8651-3e2f5f65d505",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.741733944954063\n",
            "Average of weight-2 expectation values is 0.6968862385320495\n"
          ]
        }
      ],
      "source": [
        "cut_weight_1 = np.mean(reconstructed_expvals[:num_qubits])\n",
        "cut_weight_2 = np.mean(reconstructed_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {cut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {cut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1d15b180-107a-4078-afad-d306cf3098f5",
      "metadata": {},
      "source": [
        "<span id=\"cross-verify-obtain-uncut-expectation-value\" />\n",
        "\n",
        "### Verificación cruzada: Obtener el valor esperado sin recortes\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6387268-cbd6-4766-a43d-154e461dfbc4",
      "metadata": {},
      "source": [
        "Es útil comparar la ventaja de la técnica de corte del circuito con la de no corte. Aquí calcularemos los valores de las expectativas sin cortar el circuito. Obsérvese que un circuito sin cortes de este tipo sufrirá de un gran número de puertas SWAP necesarias para implementar la operación de 2 qubits entre el primer y el último qubits. Utilizaremos la función `sampled_expectation_value` para obtener los valores de las expectativas del circuito sin cortar después de obtener la distribución de probabilidad a través de `SamplerV2`. Esto permite un uso homogéneo de la primitiva en todas las instancias. Sin embargo, tenga en cuenta que también podríamos haber utilizado `EstimatorV2` para calcular directamente los valores de las expectativas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "0fcd5c53-703d-4e67-93d5-3ee4a7870753",
      "metadata": {},
      "outputs": [],
      "source": [
        "if ansatz.num_clbits == 0:\n",
        "    ansatz.measure_all()\n",
        "\n",
        "pm_uncut = generate_preset_pass_manager(\n",
        "    optimization_level=1, backend=backend, initial_layout=init_layout\n",
        ")\n",
        "\n",
        "transpiled_circuit = pm_uncut.run(ansatz)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "00e5b011-bec5-4917-a0d3-91943cad5927",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=backend, options=options)\n",
        "uncut_job = sampler.run([transpiled_circuit])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "ec68d688-7de4-4d95-8e52-23a1eea7d94e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The job id for the uncut clifford circuit is cwxfads2ac5g008jhe7g\n"
          ]
        }
      ],
      "source": [
        "uncut_job_id = uncut_job.job_id()\n",
        "print(f\"The job id for the uncut clifford circuit is {uncut_job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1e04489f-3dc9-4253-aa9a-719952f260e3",
      "metadata": {},
      "outputs": [],
      "source": [
        "uncut_result = uncut_job.result()[0]\n",
        "uncut_counts = uncut_result.data.meas.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb150558-6571-4475-aa81-08860c2782d6",
      "metadata": {},
      "source": [
        "Ahora calcularemos los valores medios de las expectativas de todos los observables de tipo weight-1 y weight-2 Z sin corte.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "20991ea2-49a8-4258-9dd1-c064655674f1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.32494128440366965\n",
            "Average of weight-2 expectation values is 0.32340917431192656\n"
          ]
        }
      ],
      "source": [
        "uncut_expvals = [\n",
        "    sampled_expectation_value(uncut_counts, obs) for obs in paulis\n",
        "]\n",
        "\n",
        "uncut_weight_1 = np.mean(uncut_expvals[:num_qubits])\n",
        "uncut_weight_2 = np.mean(uncut_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {uncut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {uncut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed843ab6-9614-45a8-864a-932d897c0d22",
      "metadata": {},
      "source": [
        "<span id=\"visualize\" />\n",
        "\n",
        "### Visualizar\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "94eccfea-feb6-4a1c-a8fd-b7491ddb3b7d",
      "metadata": {},
      "source": [
        "Visualicemos ahora la mejora obtenida para los observables weight-1 y weight-2 al utilizar el corte de puerta para el circuito en cadena periódico\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2ba8913f-ba35-409c-bc4c-5f28e3698f20",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/2ba8913f-ba35-409c-bc4c-5f28e3698f20-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "mpl.rcParams.update(mpl.rcParamsDefault)\n",
        "\n",
        "fig = plt.subplots(figsize=(12, 8), dpi=200)\n",
        "width = 0.25\n",
        "labels = [\"Weight-1\", \"Weight-2\"]\n",
        "x = np.arange(len(labels))\n",
        "\n",
        "ideal = [-1, 1]\n",
        "cut = [cut_weight_1, cut_weight_2]\n",
        "uncut = [uncut_weight_1, uncut_weight_2]\n",
        "\n",
        "br1 = np.arange(len(ideal))\n",
        "br2 = [x + width for x in br1]\n",
        "br3 = [x + width for x in br2]\n",
        "\n",
        "plt.bar(\n",
        "    br1, ideal, width=width, edgecolor=\"k\", label=\"Ideal\", color=\"#4589ff\"\n",
        ")\n",
        "plt.bar(br2, cut, width=width, edgecolor=\"k\", label=\"Cut\", color=\"#a56eff\")\n",
        "plt.bar(\n",
        "    br3, uncut, width=width, edgecolor=\"k\", label=\"Uncut\", color=\"#009d9a\"\n",
        ")\n",
        "\n",
        "plt.axhline(y=0, color=\"k\", linestyle=\"-\")\n",
        "\n",
        "plt.xticks([r + width for r in range(len(ideal))], labels, fontsize=14)\n",
        "plt.yticks(fontsize=14)\n",
        "\n",
        "plt.legend(fontsize=14)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55de226b-1c87-48c8-b5e3-ba28e0640110",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "### Resumen\n",
        "\n",
        "En resumen, hemos calculado los valores medios de las expectativas de los observables de los tipos Z weight-1 y weight-2 para una cadena periódica 1D de 109 qubits. Para ello\n",
        "\n",
        "* creó un mapa de acoplamiento virtual añadiendo una conectividad de largo alcance entre el primer y el último qubits de la cadena 1D, y transpiló el circuito.\n",
        "  * la transpilación en esta fase nos permitió evitar la sobrecarga de transpilar cada subexperimento por separado después del corte,\n",
        "  * el uso del mapa de acoplamiento virtual nos permitió evitar puertas SWAP adicionales para la operación de 2 qubits entre el primer y el último qubits.\n",
        "* eliminó la conectividad de largo alcance del circuito transpilado mediante el corte de compuertas.\n",
        "* convertir los circuitos cortados en conjuntos de puertas base aplicando los pases de transpilación adecuados.\n",
        "* ejecutó los circuitos de corte en el dispositivo IBM Quantum utilizando una primitiva `SamplerV2` .\n",
        "* obtuvo el valor esperado reconstruyendo los resultados de los circuitos cortados.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a556c909-8cac-4500-ab52-9146b47194bf",
      "metadata": {},
      "source": [
        "<span id=\"inference\" />\n",
        "\n",
        "### Inferencia\n",
        "\n",
        "Observamos en los resultados que la media de los observables de tipo weight-1 $\\langle Z \\rangle$ y weight-2 $\\langle ZZ \\rangle$ mejoran significativamente al cortar las puertas periódicas. Nótese que este estudio no incluye ninguna técnica de supresión o mitigación de errores. La mejora observada se debe únicamente al uso adecuado del corte de compuertas para este problema. Los resultados podrían haberse mejorado aún más utilizando las técnicas de mitigación y supresión.\n",
        "\n",
        "Este estudio muestra un ejemplo del uso eficaz del corte de compuertas para mejorar el rendimiento del cálculo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6b013b53",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Encuesta tutorial\n",
        "\n",
        "Responda a esta breve encuesta para darnos su opinión sobre este tutorial. Su opinión nos ayudará a mejorar nuestra oferta de contenidos y la experiencia de los usuarios.\n",
        "\n",
        "[Enlace a la encuesta](https://your.feedback.ibm.com/jfe/form/SV_3fQQYAIjTxvIChg)\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": 120
  },
  "nbformat": 4,
  "nbformat_minor": 5
}