{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d0e7f54f-e951-44d4-8cd8-5b539cf5c91c",
      "metadata": {},
      "source": [
        "---\n",
        "title: Executor examples\n",
        "description: Practical examples of using the Executor primitive in Qiskit Runtime.\n",
        "---\n",
        "\n",
        "# Executor examples\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a53ccd93-5bca-4dfb-a8a0-fcf4ed046fa7",
      "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=\"Package versions\">\n",
        "    The code on this page was developed using the following requirements.\n",
        "    We recommend using these versions or newer.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.4.0\n",
        "    qiskit-ibm-runtime~=0.46.1\n",
        "    samplomatic~=0.18.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fed832ee-87a7-4261-874d-84002f3863b5",
      "metadata": {},
      "source": [
        "The examples in this section illustrate some common ways to use the Executor primitive. Before running these examples, follow the instructions in [Install Qiskit](/docs/guides/install-qiskit) and [Executor quickstart](/docs/guides/directed-execution-model).\n",
        "\n",
        "## Before you begin\n",
        "\n",
        "Some of the code examples on this page use `samplex`, which is part of the Samplomatic package.  Therefore, before running those code block, you must install Samplomatic, as shown in the following code block.  For more information, see the [Samplomatic documentation](https://qiskit.github.io/samplomatic).\n",
        "\n",
        "```python\n",
        "pip install samplomatic\n",
        "\n",
        "# For visualization support, include the visualization dependencies.\n",
        "# pip install samplomatic[vis]\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "19549d62-4095-458d-a691-00b9bc456ed5",
      "metadata": {},
      "source": [
        "## Example: Parameterized circuit\n",
        "\n",
        "This example illustrates how to add circuit items with parameters, as well as how to add samplex items. It consists of these steps:\n",
        "\n",
        "1. Set up the circuit: Generate and transpile the target circuit.\n",
        "2. Prepare a samplex: Group gates and measurements into annotated boxes and generate the circuit template and samplex pair.\n",
        "3. Execute: Add a circuit item and a samplex item to a `QuantumProgram` and execute both in a single job.\n",
        "\n",
        "### Set up the circuit\n",
        "\n",
        "Prepare a three-qubit GHZ state, rotate the qubits around the Pauli-Z axis, and measures the qubits in the computational basis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "caf43d3e-ed55-4805-8d19-6c0980eeb1dc",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import Parameter, QuantumCircuit\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Executor\n",
        "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "import numpy as np\n",
        "from samplomatic import build\n",
        "from samplomatic.transpiler import generate_boxing_pass_manager\n",
        "\n",
        "# Generate the circuit\n",
        "circuit = QuantumCircuit(3)\n",
        "circuit.h(0)\n",
        "circuit.h(1)\n",
        "circuit.cz(0, 1)\n",
        "circuit.h(1)\n",
        "circuit.h(2)\n",
        "circuit.cz(1, 2)\n",
        "circuit.h(2)\n",
        "circuit.rz(Parameter(\"theta\"), 0)\n",
        "circuit.rz(Parameter(\"phi\"), 1)\n",
        "circuit.rz(Parameter(\"lam\"), 2)\n",
        "circuit.measure_all()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f95bc53d-ed01-4b94-988b-1e497916d0fa",
      "metadata": {},
      "source": [
        "Specify the backend and transpile the circuit to only use instructions supported by the QPU (referred to as an instruction set architecture (ISA) circuit).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "bf633f01-372f-4519-b89d-ab4075255bd9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Initialize the service and choose a backend\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "# Transpile the circuit to ISA\n",
        "preset_pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend, optimization_level=3\n",
        ")\n",
        "isa_circuit = preset_pass_manager.run(circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fbecf32d-d1b9-4e12-ab56-0a1ede7ebf04",
      "metadata": {},
      "source": [
        "### Prepare the samplex\n",
        "\n",
        "Use the `generate_boxing_pass_manager` convenience function and its twirling parameters to group two-qubit gates and measurements into boxes and apply twirling annotations.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "8d6d64ad-12ee-4c0c-a683-d1178600d3c7",
      "metadata": {},
      "outputs": [],
      "source": [
        "boxing_pm = generate_boxing_pass_manager(\n",
        "    # Add gate twirling\n",
        "    enable_gates=True,\n",
        "    # Add measurement twirling\n",
        "    enable_measures=True,\n",
        ")\n",
        "\n",
        "boxed_circuit = boxing_pm.run(isa_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2e8fb1f9-4b15-4161-9f8f-1c24d64b0045",
      "metadata": {},
      "source": [
        "Use the `build` method to generate the template circuit and the samplex.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "90e21ca1-7f39-4636-b65e-28685b610a3c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Build the template circuit and the samplex\n",
        "template_circuit, samplex = build(boxed_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "521a7263-8d2f-46fd-a261-c23ae56aa5b5",
      "metadata": {},
      "source": [
        "### Execute the circuits\n",
        "\n",
        "Executor runs `QuantumProgram` objects. Each `QuantumProgram` can contain several items. This example adds a circuit item and a samplex item for execution. For full details, see [Executor input and output](/docs/guides/executor-input-output).\n",
        "\n",
        "The first step is to initialize an empty program, requesting `1024` shots for each configuration of each item.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "85d7d9ef-a74c-42e4-a758-0f490e29275d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Generate a quantum program\n",
        "program = QuantumProgram(shots=1024)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8869d18d-cc0e-4556-a195-cd6b590bcef7",
      "metadata": {},
      "source": [
        "Append the circuit item to the `QuantumProgram`. This circuit item consists of two parts - the ISA circuit and 10 sets of its parameter values.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "19963674-3e72-473c-b24a-228316946dc5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Append the circuit and the parameter values to the program\n",
        "program.append_circuit_item(\n",
        "    isa_circuit,\n",
        "    circuit_arguments=np.random.rand(10, 3),  # 10 sets of parameter values\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41505f83-a7a6-4ea8-a7fc-a3fb45a2992a",
      "metadata": {},
      "source": [
        "Append the samplex item to the `QuantumProgram` with these arguments:\n",
        "\n",
        "* The template circuit and the samplex generated by the `build` function\n",
        "* Ten sets of parameter values for the original circuit\n",
        "* The number of randomizations to perform\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "d6b07700-5834-4baa-a08a-434516f5bc07",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Append the template circuit and samplex as a samplex item\n",
        "program.append_samplex_item(\n",
        "    template_circuit,\n",
        "    samplex=samplex,\n",
        "    samplex_arguments={\n",
        "        \"parameter_values\": np.random.rand(\n",
        "            10, 3\n",
        "        ),  # 10 sets of parameter values\n",
        "    },\n",
        "    shape=(2, 14, 10),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f39e3e78-5953-4801-b521-dd48ae487acf",
      "metadata": {},
      "source": [
        "### Run the Executor job\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "149addb2-e76f-427c-bac4-54b6a83ddb0a",
      "metadata": {},
      "outputs": [],
      "source": [
        "# initialize an Executor with default options\n",
        "executor = Executor(mode=backend)\n",
        "\n",
        "# Submit the job\n",
        "job = executor.run(program)\n",
        "\n",
        "# Retrieve the result\n",
        "result = job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "93698fec-cff7-4153-8cbb-f7e39c972d9a",
      "metadata": {},
      "source": [
        "Retrieve the result for each task.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "77235bf8-bcac-43fb-89b0-9ba74b976053",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Access the results of the classical register of task #0, the CircuitItem\n",
        "result_0 = result[0][\"meas\"]\n",
        "\n",
        "# Access the results of the classical register of task #1, the SamplexItem\n",
        "result_1 = result[1][\"meas\"]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64063941-87b5-4c5d-bbf9-8920c24a216f",
      "metadata": {},
      "source": [
        "## Example: Perform PEC\n",
        "\n",
        "This example shows how to use a samplex item to perform probabilistic error cancellation ([PEC](/docs/guides/error-mitigation-and-suppression-techniques#pec)) for error mitigation.\n",
        "\n",
        "Consider a mirrored-version of a circuit with ten qubits and two unique layers of CX gates. These are the main tasks:\n",
        "\n",
        "* Execute the circuit with twirling.\n",
        "* Execute the circuit with PEC mitigation, as in the paper [\"Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors\"](https://arxiv.org/abs/2201.09866).\n",
        "\n",
        "The pipeline consists of these steps:\n",
        "\n",
        "1. Set up: Generate the target circuit and group its operations into boxes.\n",
        "2. Learn: Learn the noise of the instructions that we want to mitigate with PEC.\n",
        "3. Execute: Run the circuit on a backend.\n",
        "4. Analyze: Post-process and analyze the results.\n",
        "\n",
        "For comparison, we will run this mirrored circuit twice. Once with only Pauli-twirling applied, and once withe PEC mitigation applied.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  The usage for this example is approximately 10 minutes on a Heron r2 processor.\n",
        "</Admonition>\n",
        "\n",
        "### Set up the circuit\n",
        "\n",
        "Choose a backend and prepare a 10-qubit circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "f9e93b2c-154a-4d09-872d-f770bcc669c4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/executor-examples/extracted-outputs/f9e93b2c-154a-4d09-872d-f770bcc669c4-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService, Executor\n",
        "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n",
        "from qiskit.circuit import QuantumCircuit, Parameter\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from samplomatic.transpiler import generate_boxing_pass_manager\n",
        "from samplomatic import build\n",
        "\n",
        "# Initialize the service and choose a backend\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "# Prepare a circuit\n",
        "\n",
        "num_qubits = 10\n",
        "num_layers = 10\n",
        "\n",
        "qubits = list(range(num_qubits))\n",
        "circuit = QuantumCircuit(num_qubits)\n",
        "\n",
        "for layer_idx in range(num_layers):\n",
        "    circuit.rx(Parameter(f\"theta_{layer_idx}\"), qubits)\n",
        "    for i in range(num_qubits // 2):\n",
        "        circuit.cz(qubits[2 * i], qubits[2 * i + 1])\n",
        "\n",
        "    circuit.rx(Parameter(f\"phi_{layer_idx}\"), qubits)\n",
        "    for i in range(num_qubits // 2 - 1):\n",
        "        circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)\n",
        "\n",
        "circuit.draw(\"mpl\", scale=0.35, fold=100)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2d76f4f5-48b7-4123-8b6b-32eeaa06527d",
      "metadata": {},
      "source": [
        "Combine the circuit with its inverse to create a mirror circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "f8ac3f75-88ca-40f9-8382-6a427303bb8e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/executor-examples/extracted-outputs/f8ac3f75-88ca-40f9-8382-6a427303bb8e-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mirror_circuit = circuit.compose(circuit.inverse())\n",
        "mirror_circuit.measure_all()\n",
        "\n",
        "mirror_circuit.draw(\"mpl\", scale=0.35, fold=100)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2bb0692c-3d4f-4a74-807e-6b93ef4ea8d2",
      "metadata": {},
      "source": [
        "Set some parameter values:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c1974bff-c738-43a8-8e27-b85059e2428a",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "parameter_values = np.random.rand(mirror_circuit.num_parameters)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ca9f203-008b-4190-9e86-affefb303938",
      "metadata": {},
      "source": [
        "Use the pass manager to transpile the circuit to become an ISA circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "224e0d6d-9238-4fae-aad8-f051c7e34938",
      "metadata": {},
      "outputs": [],
      "source": [
        "preset_pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend,\n",
        "    optimization_level=3,\n",
        ")\n",
        "\n",
        "isa_circuit = preset_pass_manager.run(mirror_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9dd71644-df3d-4cf3-9e4b-9c7624b54961",
      "metadata": {},
      "source": [
        "Next, group gates and measurements into annotated boxes. You can do this manually or use the `generate_boxing_pass_manager` function from Samplomatic for convenience. The first circuit will only have twirling applied and therefore only needs the `Twirl` annotation. The second circuit will be run with full PEC mitigation and needs both `Twirl` and `InjectNoise` annotations.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "4afb22f1-b41f-40ed-87f7-c2d0b0f6730c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Pass manager used to create twirled-annotated boxes.\n",
        "boxing_pm = generate_boxing_pass_manager(\n",
        "    enable_gates=True,\n",
        "    enable_measures=True,\n",
        ")\n",
        "\n",
        "mirror_circuit_twirl = boxing_pm.run(isa_circuit)\n",
        "\n",
        "# Pass manager used to create a new boxed circuit with\n",
        "# both Twirl and InjectNoise annotations.\n",
        "boxing_pm = generate_boxing_pass_manager(\n",
        "    enable_gates=True,\n",
        "    enable_measures=True,\n",
        "    inject_noise_targets=\"gates\",  # no measurement mitigation\n",
        "    inject_noise_strategy=\"uniform_modification\",\n",
        ")\n",
        "\n",
        "mirror_circuit_pec = boxing_pm.run(isa_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd0c7cc5-48ba-47ab-84bc-41f832af3d8d",
      "metadata": {},
      "source": [
        "### Learn the noise\n",
        "\n",
        "To minimize the number of noise learning experiments, identify the unique instructions in the second circuit (the one with boxes annotated with `InjectNoise`). In defining uniqueness, two box instructions are equal if both of the following are true:\n",
        "\n",
        "* Their content is equal, up to single-qubit gates.\n",
        "* Their `Twirl` annotation is equal (every other annotation is disregarded).\n",
        "\n",
        "This leads to three unique instructions, namely the odd and even gate boxes, and the final measurement box.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "2f8b325a-ffa4-447a-bf32-8b26b7404b0a",
      "metadata": {},
      "outputs": [],
      "source": [
        "from samplomatic.utils import find_unique_box_instructions\n",
        "\n",
        "unique_box_instructions = find_unique_box_instructions(\n",
        "    mirror_circuit_pec.data\n",
        ")\n",
        "assert len(unique_box_instructions) == 3"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7d496c90-fb07-49e6-a233-451ad4306102",
      "metadata": {},
      "source": [
        "Initialize a `NoiseLearnerV3`, choose the learning parameters by setting its options, and run a noise learning job.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "d69204bb-4beb-4b31-b9dd-e8923889685e",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3\n",
        "\n",
        "learner = NoiseLearnerV3(backend)\n",
        "\n",
        "learner.options.shots_per_randomization = 128\n",
        "learner.options.num_randomizations = 32\n",
        "learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]\n",
        "\n",
        "learner_job = learner.run(unique_box_instructions)\n",
        "\n",
        "learner_job.job_id()\n",
        "learner_result = learner_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "efe80acb-f12d-4051-84ed-d61a5a93ca23",
      "metadata": {},
      "source": [
        "Convert `result` to the object required by the samplex by using the `result.to_dict` method.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "fd63ad99-01ad-492b-9232-91f2377f97f6",
      "metadata": {},
      "outputs": [],
      "source": [
        "noise_maps = learner_result.to_dict(\n",
        "    instructions=unique_box_instructions, require_refs=False\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89dd2022-db07-46f6-8737-644aa070dcdb",
      "metadata": {},
      "source": [
        "### Execute the circuits\n",
        "\n",
        "`Executor` runs `QuantumProgram` objects. Each `QuantumProgram` can contain several *items*, which are appended to the program. Each item is a task for the program to perform.\n",
        "\n",
        "Initialize an empty program, requesting `1000` shots for each configuration of each item.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "b48b038e-6e7d-4a6a-8f9a-df1389b644fa",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n",
        "\n",
        "# Initialize an empty QuantumProgram\n",
        "program = QuantumProgram(shots=1000)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "845a9fdb-6837-4bda-925c-8795613bb708",
      "metadata": {},
      "source": [
        "Next, build the template circuit and samplex for `mirror_circuit_twirl` and append them to the program. Also request `900` randomizations from the samplex. This means that the samplex will generate `900` sets of parameters, and each set will be executed `1000` times (the number of shots) in the QPU.\n",
        "\n",
        "This is the program's first task (result 0).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "38942fab-f68d-44ed-b613-362b38a1ca02",
      "metadata": {},
      "outputs": [],
      "source": [
        "template_twirl, samplex_twirl = build(mirror_circuit_twirl)\n",
        "\n",
        "program.append_samplex_item(\n",
        "    template_twirl,\n",
        "    samplex=samplex_twirl,\n",
        "    samplex_arguments={\"parameter_values\": parameter_values},\n",
        "    shape=(900,),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a30d1536-5339-4f19-b351-1c26891a4817",
      "metadata": {},
      "source": [
        "Similarly, append the template circuit and samplex built for `mirror_circuit_pec`, requesting `900` randomizations.  This is the program's second task (result 1).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "16bbb410-b0b7-4011-b4d0-62ffbad30f41",
      "metadata": {},
      "outputs": [],
      "source": [
        "template_pec, samplex_pec = build(mirror_circuit_pec)\n",
        "\n",
        "program.append_samplex_item(\n",
        "    template_pec,\n",
        "    samplex=samplex_pec,\n",
        "    samplex_arguments={\n",
        "        \"parameter_values\": parameter_values,\n",
        "        \"pauli_lindblad_maps\": noise_maps,\n",
        "        \"noise_scales\": {\n",
        "            ref: -1.0 for ref in noise_maps\n",
        "        },  # Set the scales to -1 for PEC\n",
        "    },\n",
        "    shape=(900,),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "abf9d679-0262-4447-b889-9b06ad3cd77d",
      "metadata": {},
      "source": [
        "Import `Executor` and submit a job.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "6231e441-87f4-480a-886a-5fdd83631e60",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Twirl result keys:\n",
            " ['meas', 'measurement_flips.meas']\n",
            "\n",
            "Shape of results: (900, 1000, 10)\n",
            "PEC result keys:\n",
            " ['meas', 'measurement_flips.meas', 'pauli_signs']\n",
            "\n",
            "Shape of results: (900, 1000, 10)\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime.executor import Executor\n",
        "\n",
        "executor = Executor(backend)\n",
        "executor_job = executor.run(program)\n",
        "\n",
        "executor_job.job_id()\n",
        "\n",
        "executor_results = executor_job.result()\n",
        "executor_results\n",
        "\n",
        "twirl_result = executor_results[0]\n",
        "\n",
        "print(f\"Twirl result keys:\\n {list(twirl_result.keys())}\\n\")\n",
        "print(f\"Shape of results: {twirl_result['meas'].shape}\")\n",
        "\n",
        "pec_result = executor_results[1]\n",
        "\n",
        "print(f\"PEC result keys:\\n {list(pec_result.keys())}\\n\")\n",
        "print(f\"Shape of results: {pec_result['meas'].shape}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "021b944c-5119-4554-b4e9-2af1cfc906d8",
      "metadata": {},
      "source": [
        "### Analyze results\n",
        "\n",
        "Finally, post-process the results to estimate the expectation values of single-qubit Pauli-Z operators acting on each of the ten active qubits (expected value: `1.0`).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "a24c1dd9-7f29-40b0-87ad-25c2a03e0431",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubit 0 -> 0.77\n",
            "Qubit 1 -> 0.76\n",
            "Qubit 2 -> 0.66\n",
            "Qubit 3 -> 0.71\n",
            "Qubit 4 -> 0.69\n",
            "Qubit 5 -> 0.67\n",
            "Qubit 6 -> 0.62\n",
            "Qubit 7 -> 0.59\n",
            "Qubit 8 -> 0.62\n",
            "Qubit 9 -> 0.68\n"
          ]
        }
      ],
      "source": [
        "# Undo measurement twirling\n",
        "twirl_result_unflipped = (\n",
        "    twirl_result[\"meas\"] ^ twirl_result[\"measurement_flips.meas\"]\n",
        ")\n",
        "\n",
        "# Calculate the expectation values of single-qubit Z operators\n",
        "exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)\n",
        "\n",
        "for qubit, val in enumerate(exp_vals):\n",
        "    print(f\"Qubit {qubit} -> {np.round(val, 2)}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "8aa25d47-c4fd-4b20-a36f-fa69d7bd0971",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubit 0 -> 0.98\n",
            "Qubit 1 -> 0.99\n",
            "Qubit 2 -> 0.96\n",
            "Qubit 3 -> 0.98\n",
            "Qubit 4 -> 0.98\n",
            "Qubit 5 -> 0.98\n",
            "Qubit 6 -> 0.98\n",
            "Qubit 7 -> 0.95\n",
            "Qubit 8 -> 0.95\n",
            "Qubit 9 -> 0.94\n"
          ]
        }
      ],
      "source": [
        "# Undo measurement twirling\n",
        "pec_result_unflipped = (\n",
        "    pec_result[\"meas\"] ^ pec_result[\"measurement_flips.meas\"]\n",
        ")\n",
        "\n",
        "# Calculate the signs for PEC mitigation\n",
        "signs = np.prod((-1) ** pec_result[\"pauli_signs\"], axis=-1)\n",
        "signs = signs.reshape((signs.shape[0], 1))\n",
        "\n",
        "# Calculate the expectation values of single-qubit Z operators as required by\n",
        "# PEC mitigation\n",
        "exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)\n",
        "\n",
        "for qubit, val in enumerate(exp_vals):\n",
        "    print(f\"Qubit {qubit} -> {np.round(val, 2)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cf25d17a-5e90-4e24-a3bf-c86f5bc3444b",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  * Review the [broadcasting](/docs/guides/primitive-input-output#broadcasting) overview.\n",
        "  * Learn how to use [Executor options](/docs/guides/executor-options).\n",
        "  * Understand the [directed execution model](/docs/guides/directed-execution-model).\n",
        "  * Review the [Samplomatic documentation](https://qiskit.github.io/samplomatic/).\n",
        "  * Learn about how to combine different error mitigation techniques when using directed execution model in the [Probabilistic error cancellation with shaded lightcones](https://qiskit.github.io/qiskit-addon-slc/tutorials/01_getting_started.html) tutorial.\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": 4
}