{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3e0865c2",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Utilizar la poselección en las cargas de trabajo\"\n",
        "description: \"Descubre cómo integrar la poselección en tu estrategia de mitigación de errores\"\n",
        "---\n",
        "\n",
        "<span id=\"use-postselection-in-workloads\" />\n",
        "\n",
        "# Utilizar la poselección en las cargas de trabajo\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23b642e2",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versiones del paquete\">\n",
        "    El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos.\n",
        "    Recomendamos utilizar estas versiones o posteriores.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.0\n",
        "    qiskit-ibm-runtime~=0.47.0\n",
        "    qiskit-addon-utils~=0.3.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b042e4d5",
      "metadata": {},
      "source": [
        "A la hora de optimizar la estrategia de mitigación de errores de una carga de trabajo, suele resultar útil descartar las mediciones que se sabe que han sido contaminadas por procesos de ruido no markovianos (correlacionados). Uno de los métodos para lograrlo consiste en añadir al circuito una etapa de posprocesamiento en la que se miden los qubits activos y los qubits «espectadores» adyacentes, se aplica una rotación lenta a cada qubit y, a continuación, se vuelven a medir. En los casos en que las dos mediciones no confirman que el qubit haya cambiado de estado como se esperaba, el disparo se descarta aplicando una máscara a los resultados.\n",
        "\n",
        "El paquete [de utilidades del complemento Qiskit](https://qiskit.github.io/qiskit-addon-utils/) ofrece un conjunto de pasadas del transpilador y una función de poselección para aplicar la máscara. Esta página ofrece orientación sobre cómo incorporar la poselección en sus cargas de trabajo cuánticas utilizando como ejemplo un estado GHZ de cuatro qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a645f8ff",
      "metadata": {},
      "source": [
        "<span id=\"create-workload\" />\n",
        "\n",
        "## CREATE WORKLOAD\n",
        "\n",
        "Empieza por preparar el circuito para ejecutarlo y compilarlo en un backend que admita puertas fraccionarias.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "68fa9100",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/post-selection/extracted-outputs/68fa9100-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.circuit import QuantumCircuit\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "circuit = QuantumCircuit(4)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.cx(1, 2)\n",
        "circuit.cx(2, 3)\n",
        "circuit.measure_all()\n",
        "\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(use_fractional_gates=True)\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "transpiled_circuit.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5adeb935",
      "metadata": {},
      "source": [
        "<span id=\"add-postselection-transpiler-passes\" />\n",
        "\n",
        "## Añadir pasadas del transpilador de poselección\n",
        "\n",
        "A continuación, crea un gestor de pases predefinidos que incluya los [`AddPostSelectionMeasures`](https://qiskit.github.io/qiskit-addon-utils/stubs/qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddSpectatorMeasures.html) pases y [`AddSpectatorMeasures`](https://qiskit.github.io/qiskit-addon-utils/stubs/qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddPostSelectionMeasures.html) del [`qiskit-addon-utils`](https://qiskit.github.io/qiskit-addon-utils/index.html) paquete. Esto añadirá al circuito una secuencia de rotaciones de ángulo `RX` pequeño (lo que, en la práctica, da lugar a una puerta larga `X` ), junto con un segundo conjunto de mediciones.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "faf50950",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/post-selection/extracted-outputs/faf50950-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.transpiler import PassManager\n",
        "from qiskit_addon_utils.noise_management.post_selection import PostSelector\n",
        "from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (\n",
        "    AddPostSelectionMeasures,\n",
        "    AddSpectatorMeasures,\n",
        ")\n",
        "\n",
        "\n",
        "post_selection_pm = PassManager(\n",
        "    [\n",
        "        AddSpectatorMeasures(backend.coupling_map, add_barrier=True),\n",
        "        AddPostSelectionMeasures(x_pulse_type=\"rx\"),\n",
        "    ]\n",
        ")\n",
        "\n",
        "template_circuit_ps = post_selection_pm.run(transpiled_circuit)\n",
        "template_circuit_ps.draw(\"mpl\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e0fdf29d",
      "metadata": {},
      "source": [
        "<span id=\"execute-quantum-program\" />\n",
        "\n",
        "## Ejecutar programa cuántico\n",
        "\n",
        "A continuación, prepara un `QuantumProgram` objeto que contenga el circuito que se va a ejecutar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "649aef44",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID: d9bjj43v6alc73ct56rg\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QuantumProgram, Executor\n",
        "\n",
        "shots = 4000\n",
        "\n",
        "program = QuantumProgram(shots=shots)\n",
        "program.append_circuit_item(template_circuit_ps)\n",
        "\n",
        "# Initialize the Executor job and run\n",
        "executor = Executor(backend)\n",
        "executor_job = executor.run(program)\n",
        "print(f\"Job ID: {executor_job.job_id()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4068dd8f",
      "metadata": {},
      "source": [
        "Ahora ya puedes interpretar los resultados. El resultado del ejecutor es un diccionario con varias claves.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "64f52429",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "KeysView(QuantumProgramItemResult({'meas': array([[False, False, False, False],\n",
              "       [ True,  True,  True,  True],\n",
              "       [ True,  True,  True,  True],\n",
              "       ...,\n",
              "       [False, False, False, False],\n",
              "       [False, False, False, False],\n",
              "       [False, False, False, False]], shape=(4000, 4)), 'spec': array([[False, False],\n",
              "       [False, False],\n",
              "       [False, False],\n",
              "       ...,\n",
              "       [False, False],\n",
              "       [False, False],\n",
              "       [False, False]], shape=(4000, 2)), 'meas_ps': array([[ True,  True,  True,  True],\n",
              "       [False, False, False, False],\n",
              "       [False, False, False, False],\n",
              "       ...,\n",
              "       [ True,  True,  True,  True],\n",
              "       [ True,  True,  True,  True],\n",
              "       [ True,  True,  True,  True]], shape=(4000, 4)), 'spec_ps': array([[ True,  True],\n",
              "       [ True,  True],\n",
              "       [ True,  True],\n",
              "       ...,\n",
              "       [ True,  True],\n",
              "       [ True,  True],\n",
              "       [ True,  True]], shape=(4000, 2))}, metadata=ItemMetadata()))"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "executor_result = executor_job.result()[0]\n",
        "executor_result.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6b460888",
      "metadata": {},
      "source": [
        "Estas claves corresponden a los qubits activos y de observación antes de las `rx` instrucciones (`meas` y `spec`) y después de las `rx` instrucciones (`meas_ps` y `spec_ps`). Cada uno de ellos es una matriz de matrices basada en el número de disparos y de qubits. En este caso, la forma es (1000, 4).\n",
        "\n",
        "<span id=\"create-postselection-mask\" />\n",
        "\n",
        "## Crear máscara de poselección\n",
        "\n",
        "A partir de estas medidas, puedes crear una máscara utilizando la [`PostSelector`](https://qiskit.github.io/qiskit-addon-utils/apidocs/qiskit_addon_utils.noise_management.html#qiskit_addon_utils.noise_management.PostSelector) clase de `qiskit-addon-utils`. Esta máscara es una matriz booleana en la que cada fotograma se marca como o `False``True` en función de una de las dos estrategias de poselección. `node`La primera estrategia utiliza la información de los qubits para decidir si se debe descartar una medición, mientras que la segunda `edge`utiliza la información sobre la conectividad entre vecinos más cercanos para tomar esta decisión.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "299d460d",
      "metadata": {},
      "outputs": [],
      "source": [
        "post_selector = PostSelector.from_circuit(\n",
        "    circuit=template_circuit_ps, coupling_map=backend.coupling_map\n",
        ")\n",
        "\n",
        "mask_node = post_selector.compute_mask(executor_result, strategy=\"node\")\n",
        "mask_edge = post_selector.compute_mask(executor_result, strategy=\"edge\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "023cb624",
      "metadata": {},
      "source": [
        "Tanto la estrategia de nodos como la de arcos suelen descartar diferentes tiros. Puedes elegir cualquiera de ellos. Este cuaderno utiliza una operación AND bit a bit, lo cual es una estrategia conservadora que solo retiene una jugada si esta supera tanto la estrategia de nodos como la de aristas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "5ec9bc4a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The combined mask: [ True  True  True ...  True  True  True]\n",
            "Percentage of the shots retained is after post selection 84.675\n"
          ]
        }
      ],
      "source": [
        "mask = mask_node & mask_edge\n",
        "print(f\"The combined mask: {mask}\")\n",
        "count_retained = 0\n",
        "\n",
        "for m in mask:\n",
        "    count_retained += m\n",
        "\n",
        "print(\n",
        "    f\"Percentage of the shots retained is after post selection \"\n",
        "    f\"{100 * count_retained / shots}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "77a9c1af",
      "metadata": {},
      "source": [
        "Compara la distribución de probabilidad con y sin poselección. El siguiente fragmento de código calcula la distribución de probabilidad antes y después de la postselección, así como la distancia entre la distribución observada y la distribución ideal.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "43704cfc",
      "metadata": {},
      "outputs": [],
      "source": [
        "counts = {}\n",
        "counts_ps = {}\n",
        "\n",
        "\n",
        "for idx, measurement in enumerate(executor_result[\"meas\"]):\n",
        "    bitstring = \"\"\n",
        "    for bit in measurement:\n",
        "        bitstring += str(int(bit))\n",
        "\n",
        "    if bitstring in counts:\n",
        "        counts[bitstring] += 1\n",
        "    else:\n",
        "        counts[bitstring] = 1\n",
        "\n",
        "    # Compute count data for postselected shots based on the mask\n",
        "    if mask[idx]:\n",
        "        bitstring = \"\"\n",
        "        for bit in measurement:\n",
        "            bitstring += str(int(bit))\n",
        "\n",
        "        if bitstring in counts_ps:\n",
        "            counts_ps[bitstring] += 1\n",
        "        else:\n",
        "            counts_ps[bitstring] = 1\n",
        "\n",
        "for key, val in counts.items():\n",
        "    counts[key] = val / shots\n",
        "\n",
        "\n",
        "for key, val in counts_ps.items():\n",
        "    counts_ps[key] = float(val / count_retained)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16551af2",
      "metadata": {},
      "source": [
        "Para demostrar cómo la postselección ha modificado tus resultados, calcula la distancia entre la distribución de probabilidad ideal y las distribuciones observadas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "b1ba31b9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Distance from ideal distribution before postselection: 0.95025\n",
            "Distance from ideal distribution before after-selection: 0.9337171538234426\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/post-selection/extracted-outputs/b1ba31b9-1.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import itertools\n",
        "from qiskit.visualization import plot_histogram\n",
        "\n",
        "bitstrings = [\"\".join(i) for i in itertools.product(\"01\", repeat=4)]\n",
        "counts_ideal = {}\n",
        "for bitstring in bitstrings:\n",
        "    counts_ideal[bitstring] = 0.0\n",
        "counts_ideal[\"1111\"] = 0.5\n",
        "counts_ideal[\"0000\"] = 0.5\n",
        "\n",
        "\n",
        "prob_distance = 0.0\n",
        "prob_distance_ps = 0.0\n",
        "\n",
        "for bitstring in counts_ideal.keys():\n",
        "    dist = 0.0\n",
        "    dist_ps = 0.0\n",
        "    if bitstring in counts:\n",
        "        dist = abs(counts[bitstring] - counts_ideal[bitstring])\n",
        "    if bitstring in counts_ps:\n",
        "        dist_ps = abs(counts_ps[bitstring] - counts_ideal[bitstring])\n",
        "    prob_distance += dist\n",
        "    prob_distance_ps += dist_ps\n",
        "\n",
        "\n",
        "print(\n",
        "    f\"Distance from ideal distribution before postselection: \"\n",
        "    f\"{1-prob_distance*0.5}\"\n",
        ")\n",
        "print(\n",
        "    f\"Distance from ideal distribution before after-selection: \"\n",
        "    f\"{1-prob_distance_ps*0.5}\"\n",
        ")\n",
        "\n",
        "\n",
        "plot_histogram([counts, counts_ps], legend=[\"Normal\", \"Post selected\"])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c205afea",
      "metadata": {},
      "source": [
        "Aunque la poselección puede mejorar considerablemente la calidad de los resultados al descartar las mediciones de resultados que se vieron afectadas por ruido no markoviano, por sí sola no constituye una solución completa para la mitigación de errores. La postselección reduce el impacto de ciertos errores al descartar los resultados de medición no válidos, pero esto conlleva un mayor esfuerzo de muestreo y no resuelve todos los mecanismos de error presentes en el hardware cuántico a corto plazo. Por lo tanto, es probable que no basarse únicamente en la poselección resulte insuficiente para circuitos más complejos o de mayor profundidad. Por el contrario, la poselección resulta más eficaz cuando se utiliza como parte de una estrategia más amplia de mitigación de errores —complementando técnicas como la mitigación de errores de medición, la compilación de circuitos sensible al ruido o la cancelación probabilística de errores— para mejorar la fiabilidad de las cargas de trabajo cuánticas, al tiempo que se logra un equilibrio entre la precisión y el coste de los recursos.\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  * Comprender cómo integrar [el aprendizaje con ruido](/docs/guides/noise-learning) en una carga de trabajo cuántica.\n",
        "  * Consulte otras técnicas disponibles para [la mitigación y la supresión de errores](/docs/guides/error-mitigation-and-suppression-techniques).\n",
        "  * Descubre cómo utilizar [los códigos espaciotemporales](/docs/tutorials/spacetime-codes) para aplicar un método de detección de errores con baja sobrecarga\n",
        "</Admonition>\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}