{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3445b0b5-7f26-4610-8e1b-5112df1fccb0",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Combinar opciones de mitigación de errores con la primitiva Estimator\"\n",
        "description: \"Combine opciones de mitigación de errores para experimentos a escala industrial utilizando QPU de 100Q+ IBM Quantum y la primitiva Estimator de Qiskit Runtime.\"\n",
        "---\n",
        "\n",
        "<span id=\"combine-error-mitigation-options-with-the-estimator-primitive\" />\n",
        "\n",
        "# Combinar opciones de mitigación de errores con la primitiva Estimator\n",
        "\n",
        "*Tiempo estimado de ejecución: 7 minutos en un procesador Heron r2 (NOTA: Se trata solo de una estimación. (El tiempo de ejecución puede variar.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "39ba4396-9111-4421-9371-542f067bc816",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Resultados del aprendizaje\n",
        "\n",
        "Recomendamos a los usuarios que se familiaricen con los siguientes temas antes de seguir este tutorial:\n",
        "\n",
        "* Los conceptos básicos del desacoplamiento dinámico, la mitigación de errores de medición, el «gate twirling» y la extrapolación sin ruido, tal y como se describen en esta [guía](/docs/guides/error-mitigation-and-suppression-techniques).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b27cb482-b77a-4953-a0a4-39df33896d12",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Requisitos previos\n",
        "\n",
        "Una vez completado este tutorial, los usuarios deberían comprender:\n",
        "\n",
        "* Cómo se implementan de forma selectiva en el hardware las técnicas de mitigación de errores mencionadas anteriormente.\n",
        "* Cómo se comparan en cuanto a su capacidad para mitigar el ruido del hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "018313d1-d959-4011-81df-59bf7c157470",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## En segundo plano\n",
        "\n",
        "Este tutorial analiza las opciones de supresión y mitigación de errores disponibles con la primitiva Estimator de Qiskit Runtime. Este tutorial muestra cómo implementar cada uno de los siguientes métodos por separado:\n",
        "\n",
        "* Desacoplamiento dinámico\n",
        "* Reducción de los errores de medición\n",
        "* Puerta giratoria\n",
        "* Extrapolación de ruido cero (ZNE)\n",
        "\n",
        "Ten en cuenta que una alternativa a la implementación individual de estas técnicas es implementarlas utilizando un [nivel de resiliencia](/docs/guides/estimator-noise-management), que `resilience_level` puede tomar los valores 0, 1 y 2:\n",
        "\n",
        "* 0: No se ha aplicado ninguna medida de mitigación.\n",
        "* 1: Se ha implementado la mitigación de errores de medición.\n",
        "* 2: Se han implementado el giro de compuertas, la mitigación de errores de medición y el ZNE.\n",
        "\n",
        "En este tutorial, construirás un circuito y un observador, y enviarás trabajos utilizando la primitiva Estimator con diferentes combinaciones de ajustes de mitigación de errores. A continuación, representará gráficamente los resultados para observar los efectos de los distintos ajustes. La mayor parte del tutorial utiliza un circuito de 10 qubits para facilitar la visualización y, al final, ampliarás el flujo de trabajo a 50 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f3d3378-8dac-489e-bf6d-292e693ec145",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de comenzar este tutorial, asegúrese de tener instalado lo siguiente:\n",
        "\n",
        "* Qiskit SDK v2.1 o posterior, con soporte [de visualización](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 o posterior (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "069658fa-fa45-48b8-ab69-2610228bf6e4",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuración\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "95a72452-e067-4ee5-bcd9-e3bfcf0de208",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "from qiskit.circuit.library import efficient_su2, unitary_overlap\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Batch, EstimatorV2 as Estimator"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d15c5fde-5046-4d86-86d8-72d1ed748c4e",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Ejemplo de simulador a pequeña escala\n",
        "\n",
        "Omitiremos este paso, ya que la mitigación de errores en tiempo de ejecución no es compatible con los simuladores.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2be0539-4f60-46e8-be1f-8869dae30888",
      "metadata": {},
      "source": [
        "<span id=\"hardware-example\" />\n",
        "\n",
        "## Ejemplo de hardware\n",
        "\n",
        "<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",
        "Este tutorial asume que el problema clásico ya se ha mapeado al cuántico. Comience construyendo un circuito y un observador para medir. Aunque las técnicas utilizadas aquí se aplican a muchos tipos diferentes de circuitos, para simplificar, este tutorial utiliza el [`efficient_su2`](/docs/api/qiskit/qiskit.circuit.library.efficient_su2) circuito incluido en la biblioteca de circuitos de Qiskit.\n",
        "\n",
        "`efficient_su2` es un circuito cuántico parametrizado diseñado para ejecutarse de manera eficiente en hardware cuántico con conectividad limitada entre qubits, sin dejar de ser lo suficientemente expresivo como para resolver problemas en ámbitos de aplicación como la optimización y la química. Se construye alternando capas de puertas de un solo qubit parametrizadas con una capa que contiene un patrón fijo de puertas de dos qubits, durante un número determinado de repeticiones. El usuario puede definir la configuración de las puertas de dos qubits. Aquí puedes utilizar el patrón `pairwise` integrado, ya que minimiza la profundidad del circuito al agrupar las puertas de dos qubits de la forma más compacta posible. Este patrón se puede ejecutar utilizando únicamente conectividad lineal entre qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "24abd7ba-bbb8-443b-9e81-866795d39a6c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/24abd7ba-bbb8-443b-9e81-866795d39a6c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "n_qubits = 10\n",
        "reps = 1\n",
        "\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "\n",
        "circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34fec53b-a8b2-461b-a20d-e8ad54b992fa",
      "metadata": {},
      "source": [
        "Como observable, tomemos el operador de Pauli $Z$ que actúa sobre el último qubit, $Z I \\cdots I$.\n",
        "Cabe señalar que el hecho de que el último qubit corresponda al primer elemento de esta cadena se debe a que Qiskit utiliza la notación «little-endian».\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "9500ba7b-4542-465f-a4e1-60165d6b6d1e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Z on the last qubit (index -1) with coefficient 1.0\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eff743-de9f-4f55-9c47-02c6d6115b80",
      "metadata": {},
      "source": [
        "En este punto, ya puedes poner en marcha el circuito y medir la magnitud observable. Sin embargo, también es necesario comparar el resultado del dispositivo cuántico con la respuesta correcta, es decir, el valor teórico de la observable, si el circuito se hubiera ejecutado sin errores. En el caso de los circuitos cuánticos pequeños, este valor se puede calcular simulando el circuito en un ordenador clásico, pero esto no es posible con circuitos más grandes, a escala industrial. Este problema se puede solucionar mediante la técnica del «circuito espejo» (también conocida como «compute-uncompute»), que resulta útil para evaluar el rendimiento de los dispositivos cuánticos.\n",
        "\n",
        "<span id=\"mirror-circuit\" />\n",
        "\n",
        "#### Circuito espejo\n",
        "\n",
        "En la técnica del circuito espejo, se concatena el circuito con su circuito inverso, que se forma invirtiendo cada puerta del circuito en orden inverso. El circuito resultante implementa el operador de identidad, que puede simularse trivialmente. Dado que la estructura del circuito original se conserva en el circuito espejo, la ejecución de éste permite hacerse una idea de cómo funcionaría el dispositivo cuántico en el circuito original.\n",
        "\n",
        "La siguiente celda de código asigna parámetros aleatorios a tu circuito y, a continuación, construye el circuito simétrico utilizando la [`unitary_overlap`](/docs/api/qiskit/qiskit.circuit.library.unitary_overlap) clase. Antes de duplicar el circuito, añádele una instrucción [de barrera](/docs/api/qiskit/circuit#qiskit.circuit.Barrier) para evitar que el transpilador fusione las dos partes del circuito a ambos lados de la barrera y dé como resultado un circuito transpilado sin ninguna puerta lógica.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "4dbde811-1ba9-47a8-85a0-dcaff054ed60",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/4dbde811-1ba9-47a8-85a0-dcaff054ed60-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Generate random parameters\n",
        "rng = np.random.default_rng(1234)\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "\n",
        "# Assign the parameters to the circuit\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "\n",
        "# Add a barrier to prevent circuit optimization of mirrored operators\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "mirror_circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a37c97de-c942-4419-9409-5bcaef9c6876",
      "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",
        "Debes optimizar tu circuito antes de ejecutarlo en hardware. Este proceso consta de varios pasos:\n",
        "\n",
        "* Elige una disposición de qubits que asigne los qubits virtuales de tu circuito a qubits físicos en el hardware.\n",
        "* Inserta puertas de intercambio según sea necesario para dirigir las interacciones entre qubits que no estén conectados.\n",
        "* Traduce las puertas de tu circuito a instrucciones [ISA (Instruction Set Architecture](/docs/guides/transpile#instruction-set-architecture) ) que puedan ejecutarse directamente en el hardware.\n",
        "* Realiza optimizaciones de circuitos para minimizar la profundidad del circuito y el número de compuertas.\n",
        "\n",
        "El transpilador integrado en Qiskit puede realizar todos estos pasos por usted. Dado que este ejemplo utiliza un circuito eficiente en términos de hardware, el transpilador debería ser capaz de elegir una disposición de qubits que no requiera la inserción de puertas de intercambio para enrutar las interacciones.\n",
        "\n",
        "Debes seleccionar el dispositivo de hardware que vas a utilizar antes de optimizar el circuito. La siguiente celda de código solicita el dispositivo menos ocupado que tenga al menos 127 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2c3ea768-0596-4db1-9b51-3ff78ec65818",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "aeaf076c-1c0a-4ec4-af66-49eff621589d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "print(backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46280e59-8c9f-4dd9-a365-6a230ceccf42",
      "metadata": {},
      "source": [
        "Puedes transpilar tu circuito al backend que elijas creando un gestor de pasadas y, a continuación, ejecutándolo en el circuito. Una forma sencilla de crear un gestor de contraseñas es utilizar la [`generate_preset_pass_manager`](/docs/api/qiskit/qiskit.transpiler.generate_preset_pass_manager) función. Consulte [«Transpilación con gestores de pasadas»](/docs/guides/transpile-with-pass-managers) para obtener una explicación más detallada sobre la transpilación con gestores de pasadas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "027e829a-44d3-455e-b2bf-8ce0d7e26b9b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/027e829a-44d3-455e-b2bf-8ce0d7e26b9b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pass_manager = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=1234\n",
        ")\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "\n",
        "isa_circuit.draw(\"mpl\", idle_wires=False, scale=0.7, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1865671e-2c7a-44dd-8938-fe4637bb5993",
      "metadata": {},
      "source": [
        "El circuito transpilado ahora contiene únicamente instrucciones ISA. Todas las puertas se han descompuesto en puertas de tipo « $\\sqrt{X}$ », rotaciones de tipo « $R_z$ » y [puertas CZ](/docs/api/qiskit/qiskit.circuit.library.CZGate).\n",
        "\n",
        "El proceso de transpilación ha mapeado los qubits virtuales del circuito a qubits físicos en el hardware. La información sobre la disposición de los qubits se almacena en el atributo `layout` del circuito transpilado. El observable también se definió en términos de qubits virtuales, por lo que es necesario aplicar esta disposición al observable, lo que se puede hacer con el método [`apply_layout`](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp#apply_layout) de `SparsePauliOp`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "12338588-a2d0-4b9c-9e49-e25ec8617b93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original observable:\n",
            "SparsePauliOp(['ZIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n",
            "\n",
            "Observable with layout applied:\n",
            "SparsePauliOp(['IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n"
          ]
        }
      ],
      "source": [
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "print(\"Original observable:\")\n",
        "print(observable)\n",
        "print()\n",
        "print(\"Observable with layout applied:\")\n",
        "print(isa_observable)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "439d4edf-d1ad-49f3-b206-320958be4f82",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Paso 3: Ejecutar utilizando Qiskit primitives\n",
        "\n",
        "Ahora estás listo para ejecutar tu circuito utilizando la primitiva Estimator.\n",
        "\n",
        "A continuación, enviará cinco trabajos distintos, empezando sin supresión ni mitigación de errores y activando sucesivamente las distintas opciones de supresión y mitigación de errores disponibles en Qiskit Runtime. Para obtener información sobre las opciones, consulte las páginas siguientes:\n",
        "\n",
        "* [Resumen de todas las opciones](/docs/api/qiskit-ibm-runtime/options)\n",
        "* [Desacoplamiento dinámico](/docs/api/qiskit-ibm-runtime/options-dynamical-decoupling-options)\n",
        "* [Resistencia, incluida la mitigación de errores de medición y la extrapolación de ruido cero (ZNE)](/docs/api/qiskit-ibm-runtime/options-resilience-options-v2)\n",
        "* [Girando](/docs/api/qiskit-ibm-runtime/options-twirling-options)\n",
        "\n",
        "Dado que estos trabajos pueden ejecutarse independientemente unos de otros, puede utilizar [el modo por lotes](/docs/guides/run-jobs-batch) para que Qiskit Runtime optimice el tiempo de su ejecución.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1114d15b-1408-4f4c-a20f-60f0da722325",
      "metadata": {},
      "outputs": [],
      "source": [
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_SS\"\n",
        "    ]  # add tag for this small scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "919517c8-6f42-48d9-a976-53c715c00a4b",
      "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",
        "Por último, puedes analizar los datos. Aquí recuperará los resultados de los trabajos, extraerá de ellos los valores de las expectativas medidas y trazará los valores, incluidas las barras de error de una desviación estándar.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "eef38976-0ca2-429a-b2dc-41aac69605f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/eef38976-0ca2-429a-b2dc-41aac69605f7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b400eae8-f38c-4566-82fe-0ccc5ee3f05d",
      "metadata": {},
      "source": [
        "A esta escala tan pequeña, es difícil ver el efecto de la mayoría de las técnicas de mitigación de errores, pero la extrapolación de ruido cero ofrece una mejora notable. Sin embargo, hay que tener en cuenta que esta mejora no es gratuita, ya que el resultado de la ZNE también tiene una barra de error mayor.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "958a5d23-ea09-4750-827c-e3bf6c3fa3f4",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Ejemplo de hardware a gran escala\n",
        "\n",
        "Al desarrollar un experimento, es útil comenzar con un circuito pequeño para facilitar las visualizaciones y simulaciones. Ahora que ha desarrollado y probado nuestro flujo de trabajo en un circuito de 10 qubits, puede ampliarlo hasta 50 qubits. La siguiente celda de código repite todos los pasos de este tutorial, pero ahora los aplica a un circuito de 50 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "d7d8408b-faf1-4eda-ab9c-bdeaab01ff53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/d7d8408b-faf1-4eda-ab9c-bdeaab01ff53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n_qubits = 50\n",
        "reps = 1\n",
        "\n",
        "# Construct circuit and observable\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")\n",
        "\n",
        "# Assign parameters to circuit\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "# Transpile circuit and observable\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "# Run jobs\n",
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_LS\"\n",
        "    ]  # add tag for this large scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)\n",
        "\n",
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32d9b036-ea0a-4484-b6ee-036ff732026b",
      "metadata": {},
      "source": [
        "Al comparar los resultados de 50 qubits con los resultados de 10 qubits de antes, puede que observe lo siguiente (sus resultados pueden diferir en las distintas ejecuciones):\n",
        "\n",
        "* Todos los experimentos arrojan resultados más cercanos al valor ideal y todas las barras de error son más pequeñas.\n",
        "* Es posible que la incorporación del desacoplamiento dinámico haya empeorado el rendimiento en comparación con el caso sin medidas de mitigación. Esto no es de extrañar, ya que el circuito es muy denso. El desacoplamiento dinámico resulta especialmente útil cuando hay largos intervalos en el circuito, durante los cuales los qubits permanecen inactivos sin que se les apliquen puertas lógicas. Cuando no existen estos intervalos, el desacoplamiento dinámico no resulta eficaz y, de hecho, puede empeorar el rendimiento debido a errores en los propios pulsos de desacoplamiento dinámico. Es posible que el circuito de 10 qubits fuera demasiado pequeño para que pudiéramos observar este efecto.\n",
        "* Con la extrapolación sin ruido, el resultado es muy cercano al valor ideal. Esto demuestra el potencial de ZNE.\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  Si este trabajo te ha parecido interesante, quizá te interese el siguiente material sobre algunas técnicas adicionales de mitigación y supresión de errores que no se han mencionado en este tutorial:\n",
        "\n",
        "  * [Cancelación probabilística de errores (PEC)](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec)\n",
        "  * [Mitigación de mediciones sin matrices ( M3 )](https://qiskit.github.io/qiskit-addon-mthree/) (véase también el tutorial «[Mitigación de errores de lectura para la primitiva Sampler mediante M3](/docs/tutorials/readout-error-mitigation-sampler/) »)\n",
        "  * [Conos de luz sombreados](https://qiskit.github.io/qiskit-addon-slc/)\n",
        "  * [Absorción del ruido propagado (PNA)](https://qiskit.github.io/qiskit-addon-pna/)\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"
    },
    "hours": 1,
    "qpuSeconds": 420
  },
  "nbformat": 4,
  "nbformat_minor": 4
}