{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "fad91a68",
      "metadata": {},
      "source": [
        "---\n",
        "title: Error mitigation\n",
        "description: Throughout this lesson, we will examine noise and how it can be mitigated on quantum computers.\n",
        "---\n",
        "\n",
        "# Quantum noise and error mitigation\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Toshinari Itoko (28 June 2024)\n",
        "\n",
        "  [Download the pdf](https://ibm.ent.box.com/public/static/a0zgies7bh91hm2lwev9o0bfeybxc6n6.zip) of the original lecture. Note that some code snippets might become deprecated since these are static images.\n",
        "\n",
        "  *Approximate QPU time to run this experiment is 1 m 40 s.*\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e936adb0-bc31-41a8-b988-02ce7e68b902",
      "metadata": {},
      "source": [
        "## 1. Introduction\n",
        "\n",
        "Throughout this lesson, we will examine noise and how it can be mitigated on quantum computers. We will begin by looking at the effects of noise using a simulator that can simulate noise in a few ways, including using noise profiles from real quantum computers. Then we will move on to real quantum computers, in which noise is inherent. We will look at the effects of error mitigation, including combinations of things like zero-noise extrapolation (ZNE) and gate-twirling.\n",
        "\n",
        "We will start by loading some packages.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "38cf024b",
      "metadata": {},
      "outputs": [],
      "source": [
        "# !pip install qiskit qiskit_aer qiskit_ibm_runtime\n",
        "# !pip install jupyter\n",
        "# !pip install matplotlib pylatexenc"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "897008ea",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "486b4d35",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'0.17.1'"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit_aer\n",
        "\n",
        "qiskit_aer.__version__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "994c44e5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'0.40.1'"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit_ibm_runtime\n",
        "\n",
        "qiskit_ibm_runtime.__version__"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ee48a826-fda3-4fb4-b50b-d69c38eb887f",
      "metadata": {
        "editable": true,
        "slideshow": {
          "slide_type": "slide"
        },
        "tags": []
      },
      "source": [
        "## 2. Noisy simulation without error mitigation\n",
        "\n",
        "Qiskit Aer is a classical simulator for quantum computing. It can simulate not only ideal execution but also noisy execution of quantum circuits. This notebook demonstrates how to run noisy simulation using Qiskit Aer:\n",
        "\n",
        "1. Build a noise model\n",
        "2. Build a noisy sampler (simulator) with the noise model\n",
        "3. Run a quantum circuit on the noisy sampler\n",
        "\n",
        "```\n",
        "noise_model = NoiseModel()\n",
        "...\n",
        "noisy_sampler = Sampler(options={\"backend_options\": {\"noise_model\": noise_model}})\n",
        "job = noisy_sampler.run([circuit])\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d1490ab8",
      "metadata": {},
      "source": [
        "### 2.1 Build a test circuit\n",
        "\n",
        "We consider toy 1-qubit circuits which just repeat X gates `d` times (`d`=0 ... 100) and measure the `Z` observable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "b4863c66",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/b4863c66-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit.circuit import QuantumCircuit\n",
        "\n",
        "MAX_DEPTH = 100\n",
        "circuits = []\n",
        "for d in range(MAX_DEPTH + 1):\n",
        "    circ = QuantumCircuit(1)\n",
        "    for _ in range(d):\n",
        "        circ.x(0)\n",
        "        circ.barrier(0)\n",
        "    circ.measure_all()\n",
        "    circuits.append(circ)\n",
        "\n",
        "display(circuits[3].draw(output=\"mpl\"))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "a366502c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "SparsePauliOp(['Z'],\n",
              "              coeffs=[1.+0.j])"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "obs = SparsePauliOp.from_list([(\"Z\", 1.0)])\n",
        "obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6200c2ed",
      "metadata": {},
      "source": [
        "### 2.2 Build a noise model\n",
        "\n",
        "To do noisy simulation, we need to specify `NoiseModel`. We show how to build `NoiseModel` in this section.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dae0c070",
      "metadata": {},
      "source": [
        "We first need to define quantum (or readout) errors to add to a noise model.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "1e0f1c12",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_aer.noise.errors import (\n",
        "    coherent_unitary_error,\n",
        "    amplitude_damping_error,\n",
        "    ReadoutError,\n",
        ")\n",
        "from qiskit.circuit.library import RXGate\n",
        "\n",
        "# Coherent (unitary) error: Over X-rotation error\n",
        "# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.coherent_unitary_error.html#qiskit_aer.noise.coherent_unitary_error\n",
        "OVER_ROTATION_ANGLE = 0.05\n",
        "coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())\n",
        "\n",
        "# Incoherent error: Amplitude dumping error\n",
        "# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.amplitude_damping_error.html#qiskit_aer.noise.amplitude_damping_error\n",
        "AMPLITUDE_DAMPING_PARAM = 0.02  # in [0, 1] (0: no error)\n",
        "incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)\n",
        "\n",
        "# Readout (measurement) error: Readout error\n",
        "# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.ReadoutError.html#qiskit_aer.noise.ReadoutError\n",
        "PREP0_MEAS1 = 0.03  # P(1|0): Probability of preparing 0 and measuring 1\n",
        "PREP1_MEAS0 = 0.08  # P(0|1): Probability of preparing 1 and measuring 0\n",
        "readout_error = ReadoutError(\n",
        "    [[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "e23c26ba",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_aer.noise import NoiseModel\n",
        "\n",
        "noise_model = NoiseModel()\n",
        "noise_model.add_quantum_error(coherent_error.compose(incoherent_error), \"x\", (0,))\n",
        "noise_model.add_readout_error(readout_error, (0,))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a786224",
      "metadata": {},
      "source": [
        "### 2.3 Build a noisy sampler with the noise model\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "f8aded6f",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_aer.primitives import SamplerV2 as Sampler\n",
        "\n",
        "noisy_sampler = Sampler(options={\"backend_options\": {\"noise_model\": noise_model}})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "922ba1ac",
      "metadata": {},
      "source": [
        "### 2.4 Run quantum circuits on the noisy sampler\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "4ae504e7",
      "metadata": {},
      "outputs": [],
      "source": [
        "job = noisy_sampler.run(circuits, shots=400)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "3b2fff25",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = job.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "4dc12337",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 389, '1': 11}"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result[0].data.meas.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e654c5e",
      "metadata": {},
      "source": [
        "### 2.5 Plot results\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1a25e394",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.title(\"Noisy simulation\")\n",
        "ds = list(range(MAX_DEPTH + 1))\n",
        "plt.plot(\n",
        "    ds,\n",
        "    [result[d].data.meas.expectation_values([\"Z\"]) for d in ds],\n",
        "    color=\"gray\",\n",
        "    linestyle=\"-\",\n",
        ")\n",
        "plt.scatter(ds, [result[d].data.meas.expectation_values([\"Z\"]) for d in ds], marker=\"o\")\n",
        "plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors=\"black\")\n",
        "plt.ylim(-1, 1)\n",
        "plt.xlabel(\"Circuit depth\")\n",
        "plt.ylabel(\"Measured <Z>\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "09ef68bb",
      "metadata": {},
      "source": [
        "### 2.6 Ideal simulation\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "041abc81",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/041abc81-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "ideal_sampler = Sampler()\n",
        "job_ideal = ideal_sampler.run(circuits)\n",
        "result_ideal = job_ideal.result()\n",
        "plt.title(\"Ideal simulation\")\n",
        "ds = list(range(MAX_DEPTH + 1))\n",
        "plt.plot(\n",
        "    ds,\n",
        "    [result_ideal[d].data.meas.expectation_values([\"Z\"]) for d in ds],\n",
        "    color=\"gray\",\n",
        "    linestyle=\"-\",\n",
        ")\n",
        "plt.scatter(\n",
        "    ds, [result_ideal[d].data.meas.expectation_values([\"Z\"]) for d in ds], marker=\"o\"\n",
        ")\n",
        "plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors=\"black\")\n",
        "plt.xlabel(\"Circuit depth\")\n",
        "plt.ylabel(\"Measured <Z>\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1e1b6e6",
      "metadata": {},
      "source": [
        "### 2.7 Exercise\n",
        "\n",
        "By tweaking the code below,\n",
        "\n",
        "* [ ] Try 25x number of shots (= 10\\_000 shots) and ensure that a smoother plot is obtained\n",
        "* [ ] Change noise parameters (OVER\\_ROTATION\\_ANGLE, AMPLITUDE\\_DAMPING\\_PARAM, PREP0\\_MEAS1, or PREP1\\_MEAS0) and see how the plot changes\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "502b9cfe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/502b9cfe-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "OVER_ROTATION_ANGLE = 0.05\n",
        "coherent_error = coherent_unitary_error(RXGate(OVER_ROTATION_ANGLE).to_matrix())\n",
        "AMPLITUDE_DAMPING_PARAM = 0.02  # in [0, 1] (0: no error)\n",
        "incoherent_error = amplitude_damping_error(AMPLITUDE_DAMPING_PARAM)\n",
        "PREP0_MEAS1 = 0.1  # P(1|0): Probability of preparing 0 and measuring 1\n",
        "PREP1_MEAS0 = 0.05  # P(0|1): Probability of preparing 1 and measuring 0\n",
        "readout_error = ReadoutError(\n",
        "    [[1 - PREP0_MEAS1, PREP0_MEAS1], [PREP1_MEAS0, 1 - PREP1_MEAS0]]\n",
        ")\n",
        "noise_model = NoiseModel()\n",
        "noise_model.add_quantum_error(coherent_error.compose(incoherent_error), \"x\", (0,))\n",
        "noise_model.add_readout_error(readout_error, (0,))\n",
        "options = {\n",
        "    \"backend_options\": {\"noise_model\": noise_model},\n",
        "}\n",
        "noisy_sampler = Sampler(options=options)\n",
        "job = noisy_sampler.run(circuits, shots=400)\n",
        "result = job.result()\n",
        "plt.title(\"Noisy simulation\")\n",
        "ds = list(range(MAX_DEPTH + 1))\n",
        "plt.plot(\n",
        "    ds,\n",
        "    [result[d].data.meas.expectation_values([\"Z\"]) for d in ds],\n",
        "    marker=\"o\",\n",
        "    linestyle=\"-\",\n",
        ")\n",
        "plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors=\"black\")\n",
        "plt.ylim(-1, 1)\n",
        "plt.xlabel(\"Depth\")\n",
        "plt.ylabel(\"Measured <Z>\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3cfe1a93",
      "metadata": {},
      "source": [
        "### 2.8 More realistic noisy simulation\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8d607bb5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<IBMBackend('ibm_strasbourg')>"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler, QiskitRuntimeService\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "real_backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")  # Eagle"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "81a67f2f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/81a67f2f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "aer = AerSimulator.from_backend(real_backend)\n",
        "noisy_sampler = Sampler(mode=aer)\n",
        "job = noisy_sampler.run(circuits)\n",
        "result = job.result()\n",
        "plt.title(\"Noisy simulation with noise model from real backend\")\n",
        "ds = list(range(MAX_DEPTH + 1))\n",
        "plt.plot(\n",
        "    ds,\n",
        "    [result[d].data.meas.expectation_values([\"Z\"]) for d in ds],\n",
        "    marker=\"o\",\n",
        "    linestyle=\"-\",\n",
        ")\n",
        "plt.hlines(0, xmin=0, xmax=MAX_DEPTH, colors=\"black\")\n",
        "plt.ylim(-1, 1)\n",
        "plt.xlabel(\"Depth\")\n",
        "plt.ylabel(\"Measured <Z>\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "567d30e9",
      "metadata": {},
      "source": [
        "## 3. Real quantum computation with error mitigation\n",
        "\n",
        "In this part, we demonstrate how to obtain error mitigated results (expectation values) using Qiskit Estimator.\n",
        "We consider 6-qubit Trotterized circuits for simulating the time evolution of one dimensional Ising model and see how the error scales with respect to the number of time steps.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "2d301499",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<IBMBackend('ibm_strasbourg')>"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")  # Eagle\n",
        "backend"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "6c90b89c",
      "metadata": {},
      "outputs": [],
      "source": [
        "NUM_QUBITS = 6\n",
        "NUM_TIME_STEPS = list(range(8))\n",
        "RX_ANGLE = 0.1\n",
        "RZZ_ANGLE = 0.1"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b1489739",
      "metadata": {},
      "source": [
        "### 3.1 Build circuits\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "1a77956e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Build circuits with different number of time steps\n",
        "circuits = []\n",
        "for n_steps in NUM_TIME_STEPS:\n",
        "    circ = QuantumCircuit(NUM_QUBITS)\n",
        "    for i in range(n_steps):\n",
        "        # rx layer\n",
        "        for q in range(NUM_QUBITS):\n",
        "            circ.rx(RX_ANGLE, q)\n",
        "        # 1st rzz layer\n",
        "        for q in range(1, NUM_QUBITS - 1, 2):\n",
        "            circ.rzz(RZZ_ANGLE, q, q + 1)\n",
        "        # 2nd rzz layer\n",
        "        for q in range(0, NUM_QUBITS - 1, 2):\n",
        "            circ.rzz(RZZ_ANGLE, q, q + 1)\n",
        "    circ.barrier()  # need not to optimize the circuit\n",
        "    # Uncompute stage\n",
        "    for i in range(n_steps):\n",
        "        for q in range(0, NUM_QUBITS - 1, 2):\n",
        "            circ.rzz(-RZZ_ANGLE, q, q + 1)\n",
        "        for q in range(1, NUM_QUBITS - 1, 2):\n",
        "            circ.rzz(-RZZ_ANGLE, q, q + 1)\n",
        "        for q in range(NUM_QUBITS):\n",
        "            circ.rx(-RX_ANGLE, q)\n",
        "    circuits.append(circ)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "94eb9062",
      "metadata": {},
      "source": [
        "To know the ideal output in advance, we use compute-uncompute circuits that consist of a first stage where the original circuit $U$ is applied, and a second stage where it is reversed $U^\\dagger$.\n",
        "Note that the ideal outcome of such circuits will trivially be the input state $|000000\\rangle$, which has the trivial expectation values for any Pauli observables, for example, $\\langle IIIIIZ \\rangle = 1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "20296b5a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/20296b5a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Print the circuit with 2 time steps\n",
        "circuits[2].draw(output=\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cde8d3ea",
      "metadata": {},
      "source": [
        "Note: As shown above, the circuit with $k$ time steps will have $4k$ two-qubit gate layers.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "af0d03e9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "SparsePauliOp(['IIIIIZ'],\n",
              "              coeffs=[1.+0.j])"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "obs = SparsePauliOp.from_sparse_list([(\"Z\", [0], 1.0)], num_qubits=NUM_QUBITS)\n",
        "obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ebd6ea9b",
      "metadata": {},
      "source": [
        "### 3.2 Transpile the circuits\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8eaa552e",
      "metadata": {},
      "source": [
        "We transpile the circuits for the backend with optimization (`optimization_level=1`).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "87b861e2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/87b861e2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "pm = generate_preset_pass_manager(optimization_level=1, backend=backend)\n",
        "isa_circuits = pm.run(circuits)\n",
        "display(isa_circuits[2].draw(\"mpl\", idle_wires=False, fold=-1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "743ad427",
      "metadata": {},
      "source": [
        "### 3.3 Execute using Estimator (with different resilience levels)\n",
        "\n",
        "Setting the resilienece level (`estimator.options.resilience_level`) is the easiest way to apply error mitigation when using Qiskit Estimator. Estimator supports the following resilience levels (as of 2024/06/28). See more details in the [Configure error mitigation](/docs/guides/configure-error-mitigation) guide.\n",
        "\n",
        "![image.png](https://eu-de.quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/error-mitigation/res_level.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "328f71f2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID (rl=0): d146vcnmya70008emprg\n",
            "Job ID (rl=1): d146vdnqf56g0081sva0\n",
            "Job ID (rl=2): d146ven5z6q00087c61g\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import Batch\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "jobs = []\n",
        "job_ids = []\n",
        "with Batch(backend=backend):\n",
        "    for resilience_level in [0, 1, 2]:\n",
        "        estimator = Estimator()\n",
        "        estimator.options.resilience_level = resilience_level\n",
        "        job = estimator.run(\n",
        "            [(circ, obs.apply_layout(circ.layout)) for circ in isa_circuits]\n",
        "        )\n",
        "        job_ids.append(job.job_id())\n",
        "        print(f\"Job ID (rl={resilience_level}): {job.job_id()}\")\n",
        "        jobs.append(job)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "1dd804e4-0d7d-4782-9559-7b3796cab121",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n",
            "DONE\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "# check job status\n",
        "for job in jobs:\n",
        "    print(job.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "d138e1e9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# REPLACE WITH YOUR OWN JOB IDS\n",
        "jobs = [service.job(job_id) for job_id in job_ids]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "ea820e98",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Get results\n",
        "results = [job.result() for job in jobs]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e3691462",
      "metadata": {},
      "source": [
        "### 3.4 Plot results\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "7527976e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/error-mitigation/extracted-outputs/7527976e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.title(\"Error mitigation with different resilience levels\")\n",
        "labels = [\"0 (No mitigation)\", \"1 (TREX)\", \"2 (ZNE + Gate twirling)\"]\n",
        "steps = NUM_TIME_STEPS\n",
        "for result, label in zip(results, labels):\n",
        "    plt.errorbar(\n",
        "        x=steps,\n",
        "        y=[result[s].data.evs for s in steps],\n",
        "        yerr=[result[s].data.stds for s in steps],\n",
        "        marker=\"o\",\n",
        "        linestyle=\"-\",\n",
        "        capsize=4,\n",
        "        label=label,\n",
        "    )\n",
        "plt.hlines(\n",
        "    1.0, min(steps), max(steps), linestyle=\"dashed\", label=\"Ideal\", colors=\"black\"\n",
        ")\n",
        "plt.xlabel(\"Time steps\")\n",
        "plt.ylabel(\"Mitigated <IIIIIZ>\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e4def30b",
      "metadata": {},
      "source": [
        "## 4. (Optional) Customize error mitigation options\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f099e16c",
      "metadata": {},
      "source": [
        "We can customize the application of error mitigation techniques via options as shown below.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "a22e6b8c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# TREX\n",
        "estimator.options.twirling.enable_measure = True\n",
        "estimator.options.twirling.num_randomizations = \"auto\"\n",
        "estimator.options.twirling.shots_per_randomization = \"auto\"\n",
        "\n",
        "# Gate twirling\n",
        "estimator.options.twirling.enable_gates = True\n",
        "# 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",
        "\n",
        "# Dynamical decoupling\n",
        "estimator.options.dynamical_decoupling.enable = True  # Default: False\n",
        "estimator.options.dynamical_decoupling.sequence_type = \"XX\"\n",
        "\n",
        "# Other options\n",
        "estimator.options.default_shots = 10_000"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "79dd06fb",
      "metadata": {},
      "source": [
        "See the following guides and API reference for the details of error mitigation options.\n",
        "\n",
        "* [Configure error mitigation](/docs/guides/configure-error-mitigation)\n",
        "* [Introduction to options](/docs/guides/runtime-options-overview)\n",
        "* [EstimatorOptions](/docs/api/qiskit-ibm-runtime/options-estimator-options)\n",
        "* [SamplerOptions](/docs/api/qiskit-ibm-runtime/options-sampler-options)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}