{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "dbb193c8-d927-434c-a199-48de57c99694",
      "metadata": {},
      "source": [
        "---\n",
        "title: Primitive inputs and outputs\n",
        "description: Understand the input and output format (including Primitive Unified Blocs or PUBs) of the primitives\n",
        "---\n",
        "\n",
        "# Primitive inputs and outputs\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33c719ad",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"New execution model, now in beta release\">\n",
        "  The beta release of a new execution model is now available. The directed execution model provides more flexibility when customizing your error mitigation workflow. See the [Directed execution model](/docs/guides/directed-execution-model) guide for more information.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "13bdfd0b-b5df-4dc8-9de5-f4ee382bc1d4",
      "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.3.0\n",
        "    qiskit-ibm-runtime~=0.43.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6962c44c-da86-45ac-a70f-bd4ceb2c262b",
      "metadata": {},
      "source": [
        "This page gives an overview of the inputs and outputs of the Qiskit Runtime primitives that execute workloads on IBM Quantum® compute resources. These primitives provide you with the ability to efficiently define vectorized workloads by using a data structure known as a **Primitive Unified Bloc (PUB)**. These PUBs are the fundamental unit of work a QPU needs to execute these workloads. They are used as inputs to the [`run()`](/docs/api/qiskit-ibm-runtime/estimator-v2#run) method for the Sampler and Estimator primitives, which execute the defined workload as a job. Then, after the job has completed, the results are returned in a format that is dependent on both the PUBs used as well as the runtime options specified from the Sampler or Estimator primitives.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "61903cfe-d221-4137-8324-9f0d3c1b517d",
      "metadata": {},
      "source": [
        "<span id=\"pubs\" />\n",
        "\n",
        "## Overview of PUBs\n",
        "\n",
        "When invoking a primitive's [`run()`](/docs/api/qiskit-ibm-runtime/estimator-v2#run) method, the main argument that is required is a `list` of one or more tuples -- one for each circuit being executed by the primitive. Each of these tuples is considered a PUB, and the required elements of each tuple in the list depends on the primitive used. The data provided to these tuples can also be arranged in a variety of shapes to provide flexibility in a workload through broadcasting -- the rules of which are described in a [following section](#broadcasting-rules).\n",
        "\n",
        "### Estimator PUB\n",
        "\n",
        "For the Estimator primitive, the format of the PUB should contain at most four values:\n",
        "\n",
        "* A single `QuantumCircuit`, which may contain one or more [`Parameter`](/docs/api/qiskit/qiskit.circuit.Parameter) objects\n",
        "* A list of one or more observables, which specify the expectation values to estimate, arranged into an array (for example, a single observable represented as a 0-d array, a list of observables as a 1-d array, and so on). The data can be in any one of the `ObservablesArrayLike` format such as `Pauli`, `SparsePauliOp`, `PauliList`, or `str`.\n",
        "  <Admonition type=\"note\">\n",
        "    If you have two commuting observables in different PUBs but with the same circuit, they will not be estimated using the same measurement. Each PUB represents a different basis for measurement, and therefore, separate measurements are required for each PUB. To ensure that commuting observables are estimated using the same measurement, they must be grouped within the same PUB.\n",
        "  </Admonition>\n",
        "* A collection of parameter values to bind the circuit against. This can be specified as a single array-like object where the last index is over circuit `Parameter` objects, or omitted (or equivalently, set to `None`) if the circuit has no `Parameter` objects.\n",
        "* (Optionally) a target precision for expectation values to estimate\n",
        "\n",
        "### Sampler PUB\n",
        "\n",
        "For the Sampler primitive, the format of the PUB tuple contains at most three values:\n",
        "\n",
        "* A single `QuantumCircuit`, which may contain one or more [`Parameter`](/docs/api/qiskit/qiskit.circuit.Parameter) objects\n",
        "  *Note: These circuits should also include measurement instructions for each of the qubits to be sampled.*\n",
        "* A collection of parameter values to bind the circuit against $\\theta_k$ (only needed if any `Parameter` objects are used that must be bound at runtime)\n",
        "* (Optionally) a number of shots to measure the circuit with\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2dbc9bd-32d3-4351-979f-fbb4e36a5185",
      "metadata": {},
      "source": [
        "***\n",
        "\n",
        "The following code demonstrates an example set of vectorized inputs to the `Estimator` primitive and executes them on an IBM® backend as a single `RuntimeJobV2 ` object.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "f9235c64-5dfc-42bd-b864-17f2bcdf77b9",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import (\n",
        "    Parameter,\n",
        "    QuantumCircuit,\n",
        "    ClassicalRegister,\n",
        "    QuantumRegister,\n",
        ")\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.primitives.containers import BitArray\n",
        "\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    EstimatorV2 as Estimator,\n",
        "    SamplerV2 as Sampler,\n",
        ")\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "# Instantiate runtime service and get\n",
        "# the least busy backend\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "# Define a circuit with two parameters.\n",
        "circuit = QuantumCircuit(2)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.ry(Parameter(\"a\"), 0)\n",
        "circuit.rz(Parameter(\"b\"), 0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.h(0)\n",
        "\n",
        "# Transpile the circuit\n",
        "pm = generate_preset_pass_manager(optimization_level=1, backend=backend)\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "layout = transpiled_circuit.layout\n",
        "\n",
        "# Now define a sweep over parameter values, the last axis of dimension 2 is\n",
        "# for the two parameters \"a\" and \"b\"\n",
        "params = np.vstack(\n",
        "    [\n",
        "        np.linspace(-np.pi, np.pi, 100),\n",
        "        np.linspace(-4 * np.pi, 4 * np.pi, 100),\n",
        "    ]\n",
        ").T\n",
        "\n",
        "# Define three observables. The inner length-1 lists cause this array of\n",
        "# observables to have shape (3, 1), rather than shape (3,) if they were\n",
        "# omitted.\n",
        "observables = [\n",
        "    [SparsePauliOp([\"XX\", \"IY\"], [0.5, 0.5])],\n",
        "    [SparsePauliOp(\"XX\")],\n",
        "    [SparsePauliOp(\"IY\")],\n",
        "]\n",
        "# Apply the same layout as the transpiled circuit.\n",
        "observables = [\n",
        "    [observable.apply_layout(layout) for observable in observable_set]\n",
        "    for observable_set in observables\n",
        "]\n",
        "\n",
        "# Estimate the expectation value for all 300 combinations of observables\n",
        "# and parameter values, where the pub result will have shape (3, 100).\n",
        "#\n",
        "# This shape is due to our array of parameter bindings having shape\n",
        "# (100, 2), combined with our array of observables having shape (3, 1).\n",
        "estimator_pub = (transpiled_circuit, observables, params)\n",
        "\n",
        "# Instantiate the new estimator object, then run the transpiled circuit\n",
        "# using the set of parameters and observables.\n",
        "estimator = Estimator(mode=backend)\n",
        "job = estimator.run([estimator_pub])\n",
        "result = job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "92e5f627-e4e9-433b-8fce-e25dff68bdef",
      "metadata": {},
      "source": [
        "### Broadcasting rules\n",
        "\n",
        "The PUBs aggregate elements from multiple arrays (observables and parameter values) by following the same broadcasting rules as NumPy. This section briefly summarizes those rules.  For a detailed explanation, see the [NumPy broadcasting rules documentation](https://numpy.org/doc/stable/user/basics.broadcasting.html).\n",
        "\n",
        "Rules:\n",
        "\n",
        "* Input arrays do not need to have the same number of dimensions.\n",
        "  * The resulting array will have the same number of dimensions as the input array with the largest dimension.\n",
        "  * The size of each dimension is the largest size of the corresponding dimension.\n",
        "  * Missing dimensions are assumed to have size one.\n",
        "* Shape comparisons start with the rightmost dimension and continue to the left.\n",
        "* Two dimensions are compatible if their sizes are equal or if one of them is 1.\n",
        "\n",
        "Examples of array pairs that broadcast:\n",
        "\n",
        "```text\n",
        "A1     (1d array):      1\n",
        "A2     (2d array):  3 x 5\n",
        "Result (2d array):  3 x 5\n",
        "\n",
        "\n",
        "A1     (3d array):  11 x 2 x 7\n",
        "A2     (3d array):  11 x 1 x 7\n",
        "Result (3d array):  11 x 2 x 7\n",
        "```\n",
        "\n",
        "Examples of array pairs that do not broadcast:\n",
        "\n",
        "```text\n",
        "A1     (1d array):  5\n",
        "A2     (1d array):  3\n",
        "\n",
        "A1     (2d array):      2 x 1\n",
        "A2     (3d array):  6 x 5 x 4 # This would work if the middle dimension were 2, but it is 5.\n",
        "```\n",
        "\n",
        "`EstimatorV2` returns one expectation value estimate for each element of the broadcasted shape.\n",
        "\n",
        "Here are some examples of common patterns expressed in terms of array broadcasting.  Their accompanying visual representation is shown in the figure that follows:\n",
        "\n",
        "Parameter value sets are represented by n x m arrays, and observable arrays are represented by one or more single-column arrays. For each example in the previous code, the parameter value sets are combined with their observable array to create the resulting expectation value estimates.\n",
        "\n",
        "* *Example 1*: (broadcast single observable) has a parameter value set that is a 5x1 array and a 1x1 observables array.  The one item in the observables array is combined with each item in the parameter value set to create a single 5x1 array where each item is a combination of the original item in the parameter value set with the item in the observables array.\n",
        "\n",
        "* *Example 2*: (zip) has a 5x1 parameter value set and a 5x1 observables array.  The output is a 5x1 array where each item is a combination of the nth item in the parameter value set with the nth item in the observables array.\n",
        "\n",
        "* *Example 3*: (outer/product) has a 1x6 parameter value set and a 4x1 observables array.  Their combination results in a 4x6 array that is created by combining each item in the parameter value set with *every* item in the observables array, and thus each parameter value becomes an entire column in the output.\n",
        "\n",
        "* *Example 4*: (Standard nd generalization) has a 3x6 parameter value set array and two 3x1 observables array.  These combine to create two 3x6 output arrays in a similar manner to the previous example.\n",
        "\n",
        "![This image illustrates several visual representations of array broadcasting](https://quantum.cloud.ibm.com/docs/images/guides/primitive-input-output/broadcasting.svg \"Visual representation of broadcasting\")\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "3cdc6e07-cc70-494b-84ef-f74585b6a201",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Broadcast single observable\n",
        "parameter_values = np.random.uniform(size=(5,))  # shape (5,)\n",
        "observables = SparsePauliOp(\"ZZZ\")  # shape ()\n",
        "# >> pub result has shape (5,)\n",
        "\n",
        "# Zip\n",
        "parameter_values = np.random.uniform(size=(5,))  # shape (5,)\n",
        "observables = [\n",
        "    SparsePauliOp(pauli) for pauli in [\"III\", \"XXX\", \"YYY\", \"ZZZ\", \"XYZ\"]\n",
        "]  # shape (5,)\n",
        "# >> pub result has shape (5,)\n",
        "\n",
        "# Outer/Product\n",
        "parameter_values = np.random.uniform(size=(1, 6))  # shape (1, 6)\n",
        "observables = [\n",
        "    [SparsePauliOp(pauli)] for pauli in [\"III\", \"XXX\", \"YYY\", \"ZZZ\"]\n",
        "]  # shape (4, 1)\n",
        "# >> pub result has shape (4, 6)\n",
        "\n",
        "# Standard nd generalization\n",
        "parameter_values = np.random.uniform(size=(3, 6))  # shape (3, 6)\n",
        "observables = [\n",
        "    [\n",
        "        [SparsePauliOp([\"XII\"])],\n",
        "        [SparsePauliOp([\"IXI\"])],\n",
        "        [SparsePauliOp([\"IIX\"])],\n",
        "    ],\n",
        "    [\n",
        "        [SparsePauliOp([\"ZII\"])],\n",
        "        [SparsePauliOp([\"IZI\"])],\n",
        "        [SparsePauliOp([\"IIZ\"])],\n",
        "    ],\n",
        "]  # shape (2, 3, 1)\n",
        "# >> pub result has shape (2, 3, 6)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a376cc24-04f3-43a6-921b-543f74ee72ca",
      "metadata": {},
      "source": [
        "<Admonition type=\"tip\" title=\"SparsePauliOp\">\n",
        "  Each `SparsePauliOp` counts as a single element in this context, regardless of the number of Paulis contained in the `SparsePauliOp`. Thus, for the purpose of these broadcasting rules, all of the following elements have the same shape:\n",
        "\n",
        "  ```text\n",
        "  a = SparsePauliOp(\"Z\") # shape ()\n",
        "  b = SparsePauliOp(\"IIIIZXYIZ\") # shape ()\n",
        "  c = SparsePauliOp.from_list([\"XX\", \"XY\", \"IZ\"]) # shape ()\n",
        "  ```\n",
        "\n",
        "  The following lists of operators, while equivalent in terms of information contained, have different shapes:\n",
        "\n",
        "  ```text\n",
        "  list1 = SparsePauliOp.from_list([\"XX\", \"XY\", \"IZ\"]) # shape ()\n",
        "  list2 = [SparsePauliOp(\"XX\"), SparsePauliOp(\"XY\"), SparsePauliOp(\"IZ\")] # shape (3, )\n",
        "  ```\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7a17dce0-1383-49c6-b9e4-46bf9a887fa6",
      "metadata": {},
      "source": [
        "## Overview of primitive outputs\n",
        "\n",
        "Once one or more PUBs are sent to a QPU for execution and a job successfully completes, the data is returned as a [`PrimitiveResult`](/docs/api/qiskit/qiskit.primitives.PrimitiveResult) container object accessed by calling the `RuntimeJobV2.result()` method. The `PrimitiveResult` contains an iterable list of [`PubResult`](/docs/api/qiskit/qiskit.primitives.PubResult) objects that contain the execution results for each PUB. Depending on the primitive used, these data will be either expectation values and their error bars in the case of the Estimator, or samples of the circuit output in the case of the Sampler.\n",
        "\n",
        "Each element of this list corresponds to each PUB submitted to the primitive's `run()` method (for example, a job submitted with 20 PUBs will return a `PrimitiveResult` object that contains a list of 20 `PubResults`, one corresponding to each PUB).\n",
        "\n",
        "Each of these `PubResult` objects possess both a `data` and a `metadata` attribute. The `data` attribute is a customized [`DataBin`](/docs/api/qiskit/qiskit.primitives.DataBin) that contains the actual measurement values, standard deviations, and so forth. This `DataBin` has various attributes depending on the shape or structure of the associated PUB as well as the error mitigation options specified by the primitive used to submit the job (for example, [ZNE](./error-mitigation-and-suppression-techniques#zero-noise-extrapolation-zne) or [PEC](./error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec)). Meanwhile, the `metadata` attribute contains information about the runtime and error mitigation options used (explained later in the [Result metadata](#result-metadata) section of this page).\n",
        "\n",
        "The following is a visual outline of the `PrimitiveResult` data structure:\n",
        "\n",
        "<Tabs>\n",
        "  <TabItem value=\"estimator\" label=\"Estimator Output\">\n",
        "    ```\n",
        "    └── PrimitiveResult\n",
        "        ├── PubResult[0]\n",
        "        │   ├── metadata\n",
        "        │   └── data  ## In the form of a DataBin object\n",
        "        │       ├── evs\n",
        "        │       │   └── List of estimated expectation values in the shape\n",
        "        |       |         specified by the first pub\n",
        "        │       └── stds\n",
        "        │           └── List of calculated standard deviations in the\n",
        "        |                 same shape as above\n",
        "        ├── PubResult[1]\n",
        "        |   ├── metadata\n",
        "        |   └── data  ## In the form of a DataBin object\n",
        "        |       ├── evs\n",
        "        |       │   └── List of estimated expectation values in the shape\n",
        "        |       |        specified by the second pub\n",
        "        |       └── stds\n",
        "        |           └── List of calculated standard deviations in the\n",
        "        |                same shape as above\n",
        "        ├── ...\n",
        "        ├── ...\n",
        "        └── ...\n",
        "    ```\n",
        "  </TabItem>\n",
        "\n",
        "  <TabItem value=\"sampler\" label=\"Sampler Output\">\n",
        "    ```\n",
        "    └── PrimitiveResult\n",
        "        ├── PubResult[0]\n",
        "        │   ├── metadata\n",
        "        │   └── data  ## In the form of a DataBin object\n",
        "        │       ├── NAME_OF_CLASSICAL_REGISTER\n",
        "        │       │   └── BitArray of count data (default is 'meas')\n",
        "        |       |\n",
        "        │       └── NAME_OF_ANOTHER_CLASSICAL_REGISTER\n",
        "        │           └── BitArray of count data (exists only if more than one\n",
        "        |                 ClassicalRegister was specified in the circuit)\n",
        "        ├── PubResult[1]\n",
        "        |   ├── metadata\n",
        "        |   └── data  ## In the form of a DataBin object\n",
        "        |       └── NAME_OF_CLASSICAL_REGISTER\n",
        "        |           └── BitArray of count data for second pub\n",
        "        ├── ...\n",
        "        ├── ...\n",
        "        └── ...\n",
        "    ```\n",
        "  </TabItem>\n",
        "</Tabs>\n",
        "\n",
        "Put simply, a single job returns a `PrimitiveResult` object and contains a list of one or more `PubResult` objects. These `PubResult` objects then store the measurement data for each PUB that was submitted to the job.\n",
        "\n",
        "Each `PubResult` possesses different formats and attributes based on the type of primitive that was used for the job. The specifics are explained below.\n",
        "\n",
        "### Estimator output\n",
        "\n",
        "Each `PubResult` for the Estimator primitive contains at least an array of expectation values (`PubResult.data.evs`) and associated standard deviations (either `PubResult.data.stds` or `PubResult.data.ensemble_standard_error` depending on the `resilience_level` used), but can contain more data depending on the error mitigation options that were specified.\n",
        "\n",
        "The below code snippet describes the `PrimitiveResult` (and associated `PubResult`) format for the job created above.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "960a78c7-3602-4c69-bdfa-18fd8ba993b6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The result of the submitted job had 1 PUB and has a value:\n",
            " PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})\n",
            "\n",
            "The associated PubResult of this job has the following data bins:\n",
            " DataBin(evs=np.ndarray(<shape=(3, 100), dtype=float64>), stds=np.ndarray(<shape=(3, 100), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 100), dtype=float64>), shape=(3, 100))\n",
            "\n",
            "And this DataBin has attributes: dict_keys(['evs', 'stds', 'ensemble_standard_error'])\n",
            "Recall that this shape is due to our array of parameter binding sets having shape (100, 2) -- where 2 is the\n",
            "         number of parameters in the circuit -- combined with our array of observables having shape (3, 1). \n",
            "\n",
            "The expectation values measured from this PUB are: \n",
            "[[-0.00185829  0.13833276  0.28097007  0.39487342  0.48342752  0.55449767\n",
            "   0.58814279  0.5677047   0.56992249  0.51569105  0.48012882  0.42362387\n",
            "   0.37421402  0.31408051  0.24739516  0.25011306  0.25239107  0.24919774\n",
            "   0.28517038  0.32788623  0.36342829  0.45550603  0.49481248  0.51885602\n",
            "   0.55622858  0.55590293  0.5562795   0.52738193  0.48610605  0.43466825\n",
            "   0.35987686  0.34744609  0.27380302  0.26170721  0.22644609  0.2393554\n",
            "   0.27132135  0.3281535   0.34314337  0.43730789  0.47625639  0.53221496\n",
            "   0.57145668  0.59219885  0.55184916  0.5148471   0.4601295   0.37767197\n",
            "   0.23658421  0.12158346 -0.03378715 -0.15039768 -0.31459051 -0.4088553\n",
            "  -0.48970771 -0.56080254 -0.59122344 -0.6117154  -0.572483   -0.51822532\n",
            "  -0.48771317 -0.44306794 -0.38860802 -0.32765804 -0.32313262 -0.26075804\n",
            "  -0.2760194  -0.31373299 -0.31756226 -0.36604281 -0.41199065 -0.44364942\n",
            "  -0.49183676 -0.5107731  -0.51311754 -0.52524114 -0.50503153 -0.45843084\n",
            "  -0.42155218 -0.390821   -0.34096831 -0.28431683 -0.26173344 -0.25027201\n",
            "  -0.28774795 -0.29595752 -0.32683538 -0.38118417 -0.44192273 -0.53013426\n",
            "  -0.55722925 -0.57754549 -0.6102492  -0.60420214 -0.54630362 -0.50158901\n",
            "  -0.39826435 -0.26517771 -0.13679857 -0.03799143]\n",
            " [ 0.0028399   0.04117856  0.13631523  0.20731274  0.26695066  0.32658857\n",
            "   0.37912673  0.39616613  0.49840256  0.51615193  0.61057863  0.65601704\n",
            "   0.73695421  0.76038339  0.76251331  0.82641108  0.90308839  0.88959886\n",
            "   0.91728789  0.96556621  0.96343628  1.02520412  1.0486333   1.03443379\n",
            "   1.03159389  1.02165424  1.0486333   1.02307419  1.01100461  0.99609514\n",
            "   0.96343628  0.96698616  0.93006745  0.90095847  0.86758963  0.79943202\n",
            "   0.78523252  0.74263401  0.65459709  0.66808662  0.57933972  0.53390131\n",
            "   0.47852325  0.44515442  0.32019879  0.25275115  0.24494143  0.16471424\n",
            "   0.10436635  0.05466809 -0.02768903 -0.08661697 -0.17678381 -0.2314519\n",
            "  -0.28115016 -0.35285765 -0.40823571 -0.51970181 -0.53248136 -0.54526092\n",
            "  -0.65743699 -0.68725595 -0.75044373 -0.75967341 -0.84132055 -0.86900958\n",
            "  -0.91373802 -0.97550586 -0.9542066  -1.00035499 -1.01029464 -1.02662407\n",
            "  -1.03940362 -1.01881434 -1.04153355 -1.0685126  -1.0401136  -1.01739439\n",
            "  -1.01029464 -0.99964501 -0.98189563 -0.94781683 -0.90592829 -0.84913028\n",
            "  -0.87326944 -0.80937167 -0.77387291 -0.71920483 -0.67731629 -0.67944622\n",
            "  -0.57791977 -0.49343273 -0.47852325 -0.39758608 -0.31948882 -0.29179979\n",
            "  -0.19169329 -0.14625488 -0.06105786 -0.04046858]\n",
            " [-0.00655648  0.23548696  0.42562491  0.58243409  0.69990438  0.78240678\n",
            "   0.79715886  0.73924327  0.64144243  0.51523016  0.34967901  0.19123071\n",
            "   0.01147384 -0.13222237 -0.26772299 -0.32618495 -0.39830624 -0.39120339\n",
            "  -0.34694714 -0.30979374 -0.2365797  -0.11419205 -0.05900833  0.00327824\n",
            "   0.08086327  0.09015162  0.06392569  0.03168966 -0.03879251 -0.12675864\n",
            "  -0.24368256 -0.27209398 -0.38246141 -0.37754405 -0.41469745 -0.32072121\n",
            "  -0.24258981 -0.086327    0.03168966  0.20652916  0.37317306  0.53052862\n",
            "   0.66439011  0.73924327  0.78349952  0.77694304  0.67531758  0.5906297\n",
            "   0.36880208  0.18849884 -0.03988526 -0.21417839 -0.45239721 -0.58625871\n",
            "  -0.69826526 -0.76874744 -0.77421117 -0.703729   -0.61248463 -0.49118973\n",
            "  -0.31798935 -0.19887993 -0.0267723   0.10435733  0.19505532  0.34749351\n",
            "   0.36169922  0.34803989  0.31908209  0.26826936  0.18631335  0.13932523\n",
            "   0.05573009 -0.00273187  0.01529846  0.01803032  0.03005054  0.10053271\n",
            "   0.16719027  0.21800301  0.29995902  0.37918317  0.38246141  0.34858626\n",
            "   0.29777353  0.21745663  0.12020216 -0.0431635  -0.20652916 -0.38082229\n",
            "  -0.53653872 -0.66165824 -0.74197514 -0.81081819 -0.77311843 -0.71137823\n",
            "  -0.6048354  -0.38410053 -0.21253927 -0.03551427]]\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"The result of the submitted job had {len(result)} PUB and has a value:\\n {result}\\n\"\n",
        ")\n",
        "print(\n",
        "    f\"The associated PubResult of this job has the following data bins:\\n {result[0].data}\\n\"\n",
        ")\n",
        "print(f\"And this DataBin has attributes: {result[0].data.keys()}\")\n",
        "print(\n",
        "    \"Recall that this shape is due to our array of parameter binding sets having shape (100, 2) -- where 2 is the\\n\\\n",
        "         number of parameters in the circuit -- combined with our array of observables having shape (3, 1). \\n\"\n",
        ")\n",
        "print(\n",
        "    f\"The expectation values measured from this PUB are: \\n{result[0].data.evs}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "389bd235-c6c7-4dc0-87c5-272b85cfaae0",
      "metadata": {},
      "source": [
        "#### How the Estimator calculates error\n",
        "\n",
        "In addition to the estimate of the mean of the observables passed in the input PUBs (the `evs` field of the `DataBin`), the Estimator also attempts to deliver an estimate of the error associated with those expectation values. All estimator queries will populate the `stds` field with a quantity like the standard error of the mean for each expectation value, but some error mitigation options produce additional information, such as `ensemble_standard_error`.\n",
        "\n",
        "Consider a single observable $\\mathcal{O}$. In the absence of [ZNE](/docs/guides/error-mitigation-and-suppression-techniques#zero-noise-extrapolation-zne), you can think of each shot of the Estimator execution as providing a point estimate of the expectation value $\\langle \\mathcal{O} \\rangle$. If the pointwise estimates are in a vector `Os`, then the value returned in `ensemble_standard_error` is equivalent to the following (in which $\\sigma_{\\mathcal{O}}$ is the [standard deviation of the expectation value](/docs/api/qiskit/qiskit.primitives.BackendEstimatorV2) estimate and $N_{shots}$ is the number of shots):\n",
        "\n",
        "$\\frac{ \\sigma_{\\mathcal{O}} }{ \\sqrt{N_{shots}} },$\n",
        "\n",
        "which treats all shots as part of a single ensemble. If you requested gate [twirling](/docs/guides/error-mitigation-and-suppression-techniques#pauli-twirling) (`twirling.enable_gates = True`), you can sort the pointwise estimates of $\\langle \\mathcal{O} \\rangle$ into sets that share a common twirl. Call these sets of estimates `O_twirls`, and there are `num_randomizations` (number of twirls) of them. Then `stds` is the standard error of the mean of `O_twirls`, as in\n",
        "\n",
        "$\\frac{ \\sigma_{\\mathcal{O}} }{ \\sqrt{N_{twirls}} },$\n",
        "\n",
        "where $\\sigma_{\\mathcal{O}}$ is the standard deviation of `O_twirls` and $N_{twirls}$ is the number of twirls. When you do not enable twirling, `stds` and `ensemble_standard_error` are equal.\n",
        "\n",
        "If you enable ZNE, then the `stds` described above become weights in a non-linear regression to an extrapolator model. What finally gets returned in the `stds` in this case is the uncertainty of the fit model evaluated at a noise factor of zero. When there is a poor fit, or large uncertainty in the fit, the reported `stds` can become very large. When ZNE is enabled, `pub_result.data.evs_noise_factors` and `pub_result.data.stds_noise_factors` are also populated, so that you can do your own extrapolation.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "df8faff8-4558-4f72-bd80-8615ce1ce3c5",
      "metadata": {},
      "source": [
        "### Sampler output\n",
        "\n",
        "When a Sampler job is completed successfully, the returned [`PrimitiveResult`](/docs/api/qiskit/qiskit.primitives.PrimitiveResult) object contains a list of [`SamplerPubResult`](/docs/api/qiskit/qiskit.primitives.SamplerPubResult)s, one per PUB. The data bins of these `SamplerPubResult` objects are dict-like objects that contain one `BitArray` per `ClassicalRegister` in the circuit.\n",
        "\n",
        "The `BitArray` class is a container for ordered shot data. In more detail, it stores the sampled bitstrings as bytes inside a two-dimensional array. The left-most axis of this array runs over ordered shots, while the right-most axis runs over bytes.\n",
        "\n",
        "As a first example, let us look at the following ten-qubit circuit:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "5c787874-1125-4381-b55e-7663caa92ec8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Databin: DataBin(meas=BitArray(<shape=(), num_shots=4096, num_bits=10>))\n",
            "\n",
            "BitArray: BitArray(<shape=(), num_shots=4096, num_bits=10>)\n",
            "\n",
            "The shape of register `meas` is (4096, 2).\n",
            "\n",
            "The bytes in register `alpha`, shot by shot:\n",
            "[[  3 238]\n",
            " [  0  12]\n",
            " [  0   0]\n",
            " ...\n",
            " [  3 255]\n",
            " [  0   1]\n",
            " [  0   0]]\n",
            "\n"
          ]
        }
      ],
      "source": [
        "# generate a ten-qubit GHZ circuit\n",
        "circuit = QuantumCircuit(10)\n",
        "circuit.h(0)\n",
        "circuit.cx(range(0, 9), range(1, 10))\n",
        "\n",
        "# append measurements with the `measure_all` method\n",
        "circuit.measure_all()\n",
        "\n",
        "# transpile the circuit\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "\n",
        "# run the Sampler job and retrieve the results\n",
        "sampler = Sampler(mode=backend)\n",
        "job = sampler.run([transpiled_circuit])\n",
        "result = job.result()\n",
        "\n",
        "# the data bin contains one BitArray\n",
        "data = result[0].data\n",
        "print(f\"Databin: {data}\\n\")\n",
        "\n",
        "# to access the BitArray, use the key \"meas\", which is the default name of\n",
        "# the classical register when this is added by the `measure_all` method\n",
        "array = data.meas\n",
        "print(f\"BitArray: {array}\\n\")\n",
        "print(f\"The shape of register `meas` is {data.meas.array.shape}.\\n\")\n",
        "print(f\"The bytes in register `alpha`, shot by shot:\\n{data.meas.array}\\n\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c8f3cd8f-4f43-448b-b8bc-870c3b03797f",
      "metadata": {},
      "source": [
        "It can sometimes be convenient to convert away from the bytes format in the `BitArray` to bitstrings. The `get_count` method returns a dictionary mapping bitstrings to the number of times that they occurred.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "d152f7ca-daf3-4c24-9595-10d077de8f08",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Counts: {'1111101110': 19, '0000001100': 2, '0000000000': 1324, '1111111110': 182, '1111111111': 1000, '0111111111': 50, '1111111011': 54, '0000000100': 16, '1110000000': 81, '1011111111': 46, '0000000001': 150, '1111110000': 30, '1100101110': 1, '0000001111': 26, '0000100001': 3, '0111111101': 1, '1111100000': 28, '0000000010': 54, '0000000011': 32, '1111110111': 29, '1110000010': 5, '0000100000': 17, '1111101111': 58, '1111111000': 13, '1111111010': 14, '1110111111': 22, '0010000000': 81, '1111111100': 31, '0001111111': 57, '1011111110': 11, '0011111111': 77, '1101111111': 18, '1110010001': 1, '1110100001': 1, '1111011110': 3, '0000000111': 16, '1010000000': 5, '1101111000': 1, '0110100001': 2, '1111111101': 21, '1100000000': 43, '0000001010': 1, '0001111000': 2, '0001111011': 2, '0100000000': 67, '0000001110': 8, '0011111110': 13, '0000010000': 25, '0000011111': 26, '1111111001': 8, '0000111111': 17, '1011111101': 1, '0000100110': 1, '1111110110': 7, '0110000000': 8, '1011101111': 1, '0101101111': 1, '1111011111': 9, '0100001000': 2, '1110001111': 3, '1110111110': 8, '0001101111': 5, '1000000000': 14, '0000010001': 5, '1110110000': 2, '1110001100': 1, '0011110011': 1, '1101111110': 3, '0000000110': 3, '0000001000': 9, '0111111110': 5, '0010111111': 9, '1100011000': 1, '1110000100': 3, '1011101110': 1, '0010111110': 1, '0001111110': 9, '1111100001': 1, '1111000000': 10, '0001110010': 1, '1111101101': 1, '1110000001': 9, '0010000010': 6, '0000011011': 1, '0111111000': 1, '0111101111': 2, '0000101111': 1, '0000101110': 1, '0111111100': 2, '1010111111': 1, '0011111100': 3, '0100111011': 1, '0100000001': 7, '0111111010': 1, '1110001110': 1, '1100000010': 2, '0111101110': 3, '1110100000': 3, '0010000100': 1, '0011110000': 1, '0011101110': 1, '1111000111': 1, '1100000001': 4, '1111110101': 1, '0111100000': 1, '0001100000': 3, '1011111011': 2, '1111110001': 6, '0010000001': 4, '0010001111': 1, '0010000110': 1, '1111000001': 1, '0111111011': 3, '0110000001': 2, '1100011110': 2, '0011111000': 2, '1111110100': 1, '0101111111': 2, '1001111111': 1, '0000001011': 4, '1011110111': 1, '0000001101': 2, '0111000000': 1, '0010000011': 1, '1011110000': 1, '1100000110': 1, '0111110000': 2, '1101111100': 2, '1111100111': 1, '0010100000': 1, '0010101111': 1, '0011010111': 1, '0000011110': 2, '1000000001': 1, '1101110111': 1, '0101000000': 1, '1111011010': 1, '0000111110': 4, '1111110010': 1, '1010001111': 1, '0010011111': 1, '1110001011': 1, '1011100001': 2, '0111101100': 1, '1100011111': 2, '1110000111': 2, '1101101111': 1, '0001000000': 2, '1000001111': 1, '0100000011': 1, '0111110111': 2, '1110111100': 1, '0111110110': 2, '0011110111': 2, '0011111011': 2, '0101111110': 2, '0100001011': 1, '0111001111': 1, '0101111011': 1, '0010000111': 1, '0110111111': 1, '1011111000': 1, '1111110011': 2, '1100010000': 1, '0000011010': 1, '0000011000': 1, '1100111110': 1, '0100000010': 3, '0001101110': 2, '0110000010': 1, '1111011100': 1, '0000111100': 1, '0000000101': 1, '0000111011': 1, '0001111010': 1, '0100111111': 1, '1111101011': 2, '0000110000': 1, '1110000011': 2, '1111100011': 1, '1100000111': 1, '0100000110': 1, '1110110111': 1, '0101111000': 1}\n"
          ]
        }
      ],
      "source": [
        "# optionally, convert away from the native BitArray format to a dictionary format\n",
        "counts = data.meas.get_counts()\n",
        "print(f\"Counts: {counts}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "514e0ac0-b42e-4f26-b608-c223dfed7915",
      "metadata": {},
      "source": [
        "When a circuit contains more than one classical register, the results are stored in different `BitArray` objects. The following example modifies the previous snippet by splitting the classical register into two distinct registers:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "4602925e-c69d-45b2-9e06-fe8bffe0a797",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "BitArray for register 'alpha': BitArray(<shape=(), num_shots=4096, num_bits=1>)\n",
            "BitArray for register 'beta': BitArray(<shape=(), num_shots=4096, num_bits=9>)\n"
          ]
        }
      ],
      "source": [
        "# generate a ten-qubit GHZ circuit with two classical registers\n",
        "circuit = QuantumCircuit(\n",
        "    qreg := QuantumRegister(10),\n",
        "    alpha := ClassicalRegister(1, \"alpha\"),\n",
        "    beta := ClassicalRegister(9, \"beta\"),\n",
        ")\n",
        "circuit.h(0)\n",
        "circuit.cx(range(0, 9), range(1, 10))\n",
        "\n",
        "# append measurements with the `measure_all` method\n",
        "circuit.measure([0], alpha)\n",
        "circuit.measure(range(1, 10), beta)\n",
        "\n",
        "# transpile the circuit\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "\n",
        "# run the Sampler job and retrieve the results\n",
        "sampler = Sampler(mode=backend)\n",
        "job = sampler.run([transpiled_circuit])\n",
        "result = job.result()\n",
        "\n",
        "# the data bin contains two BitArrays, one per register, and can be accessed\n",
        "# as attributes using the registers' names\n",
        "data = result[0].data\n",
        "print(f\"BitArray for register 'alpha': {data.alpha}\")\n",
        "print(f\"BitArray for register 'beta': {data.beta}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a65c6078-c5c2-4087-b57a-5680148ce333",
      "metadata": {},
      "source": [
        "#### Leveraging `BitArray` objects for performant post-processing\n",
        "\n",
        "Since arrays generally offer better performance compared to dictionaries, it is advisable to perform any post-processing directly on the `BitArray` objects rather than on dictionaries of counts. The `BitArray` class offers a range of methods to perform some common post-processing operations:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3802cf7e-4d71-4e1f-88f5-3beb294fc163",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The shape of register `alpha` is (4096, 1).\n",
            "The bytes in register `alpha`, shot by shot:\n",
            "[[1]\n",
            " [1]\n",
            " [0]\n",
            " ...\n",
            " [0]\n",
            " [1]\n",
            " [0]]\n",
            "\n",
            "The shape of register `beta` is (4096, 2).\n",
            "The bytes in register `beta`, shot by shot:\n",
            "[[  1 255]\n",
            " [  1 255]\n",
            " [  1 255]\n",
            " ...\n",
            " [  1 252]\n",
            " [  1 253]\n",
            " [  0   0]]\n",
            "\n",
            "The shape of `beta` after post-selection is (0, 2).\n",
            "The bytes in `beta` after post-selection:\n",
            "[]\n",
            "The shape of `beta` after bit-wise slicing is (4096, 1).\n",
            "The bytes in `beta` after bit-wise slicing:\n",
            "[[7]\n",
            " [7]\n",
            " [7]\n",
            " ...\n",
            " [4]\n",
            " [5]\n",
            " [0]]\n",
            "\n",
            "The shape of `beta` after shot-wise slicing is (5, 2).\n",
            "The bytes in `beta` after shot-wise slicing:\n",
            "[[  1 255]\n",
            " [  1 255]\n",
            " [  1 255]\n",
            " [  0   8]\n",
            " [  0   0]]\n",
            "\n",
            "Exp. val. for observable `SparsePauliOp(['ZZZZZZZZZ'],\n",
            "              coeffs=[1.+0.j])` is: 0.10107421875\n",
            "Exp. val. for observable `SparsePauliOp(['IIIIIIIIZ'],\n",
            "              coeffs=[1.+0.j])` is: 0.0625\n",
            "\n",
            "The shape of the merged results is (4096, 2).\n",
            "The bytes of the merged results:\n",
            "[[  3 255]\n",
            " [  3 255]\n",
            " [  3 254]\n",
            " ...\n",
            " [  3 248]\n",
            " [  3 251]\n",
            " [  0   0]]\n",
            "\n"
          ]
        }
      ],
      "source": [
        "print(f\"The shape of register `alpha` is {data.alpha.array.shape}.\")\n",
        "print(f\"The bytes in register `alpha`, shot by shot:\\n{data.alpha.array}\\n\")\n",
        "\n",
        "print(f\"The shape of register `beta` is {data.beta.array.shape}.\")\n",
        "print(f\"The bytes in register `beta`, shot by shot:\\n{data.beta.array}\\n\")\n",
        "\n",
        "# post-select the bitstrings of `beta` based on having sampled \"1\" in `alpha`\n",
        "mask = data.alpha.array == \"0b1\"\n",
        "ps_beta = data.beta[mask[:, 0]]\n",
        "print(f\"The shape of `beta` after post-selection is {ps_beta.array.shape}.\")\n",
        "print(f\"The bytes in `beta` after post-selection:\\n{ps_beta.array}\")\n",
        "\n",
        "# get a slice of `beta` to retrieve the first three bits\n",
        "beta_sl_bits = data.beta.slice_bits([0, 1, 2])\n",
        "print(\n",
        "    f\"The shape of `beta` after bit-wise slicing is {beta_sl_bits.array.shape}.\"\n",
        ")\n",
        "print(f\"The bytes in `beta` after bit-wise slicing:\\n{beta_sl_bits.array}\\n\")\n",
        "\n",
        "# get a slice of `beta` to retrieve the bytes of the first five shots\n",
        "beta_sl_shots = data.beta.slice_shots([0, 1, 2, 3, 4])\n",
        "print(\n",
        "    f\"The shape of `beta` after shot-wise slicing is {beta_sl_shots.array.shape}.\"\n",
        ")\n",
        "print(\n",
        "    f\"The bytes in `beta` after shot-wise slicing:\\n{beta_sl_shots.array}\\n\"\n",
        ")\n",
        "\n",
        "# calculate the expectation value of diagonal operators on `beta`\n",
        "ops = [SparsePauliOp(\"ZZZZZZZZZ\"), SparsePauliOp(\"IIIIIIIIZ\")]\n",
        "exp_vals = data.beta.expectation_values(ops)\n",
        "for o, e in zip(ops, exp_vals):\n",
        "    print(f\"Exp. val. for observable `{o}` is: {e}\")\n",
        "\n",
        "# concatenate the bitstrings in `alpha` and `beta` to \"merge\" the results of the two\n",
        "# registers\n",
        "merged_results = BitArray.concatenate_bits([data.alpha, data.beta])\n",
        "print(f\"\\nThe shape of the merged results is {merged_results.array.shape}.\")\n",
        "print(f\"The bytes of the merged results:\\n{merged_results.array}\\n\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f5840476-04ec-4d3b-bdb9-d166ccd8cd11",
      "metadata": {},
      "source": [
        "## Result metadata\n",
        "\n",
        "In addition to the execution results, both the `PrimitiveResult` and `PubResult` objects contain a metadata attribute about the job that was submitted. The metadata containing information for all submitted PUBs (such as the various [runtime options](/docs/api/qiskit-ibm-runtime/options) available) can be found in the `PrimitiveResult.metatada`, while the metadata specific to each PUB is found in `PubResult.metadata`.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  In the metadata field, primitive implementations can return any information about execution that is relevant to them, and there are no key-value pairs that are guaranteed by the base primitive. Thus, the returned metadata might be different in different primitive implementations.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "68268c70-d76a-47b9-af6d-5cfe7966abae",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The metadata of the PrimitiveResult is:\n",
            "'execution' : {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2026-03-01 07:55:28', stop='2026-03-01 07:55:30', size=4096>)])},\n",
            "'version' : 2,\n",
            "\n",
            "The metadata of the PubResult result is:\n",
            "'circuit_metadata' : {},\n"
          ]
        }
      ],
      "source": [
        "# Print out the results metadata\n",
        "print(\"The metadata of the PrimitiveResult is:\")\n",
        "for key, val in result.metadata.items():\n",
        "    print(f\"'{key}' : {val},\")\n",
        "\n",
        "print(\"\\nThe metadata of the PubResult result is:\")\n",
        "for key, val in result[0].metadata.items():\n",
        "    print(f\"'{key}' : {val},\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2f26ff75-e1ba-45ef-97c1-c5dde36400cf",
      "metadata": {},
      "source": [
        "For Sampler jobs, you can also review the result metadata to understand when certain data was run; this is called the [*execution span.*](monitor-job#execution-spans)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "celltoolbar": "Raw Cell Format",
    "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
}