{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "4a99b08b-5a4e-4c2c-ba72-b932d8510bf2",
      "metadata": {},
      "source": [
        "---\n",
        "title: Estimator inputs and outputs\n",
        "description: Understand the input and output format of the Estimator primitive\n",
        "---\n",
        "\n",
        "# Estimator inputs and outputs\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f04fa5d-aae1-4a05-832b-be5e0f03f5b2",
      "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",
        "<details>\n",
        "  <summary><b>Package versions</b></summary>\n",
        "\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",
        "</details>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd5b8a3a-fc21-4eaf-923c-1576db19d569",
      "metadata": {},
      "source": [
        "This page gives an overview of the inputs and outputs of the Qiskit Runtime Estimator primitive, which executes workloads on IBM Quantum® compute resources. Estimator lets you efficiently define vectorized workloads by using a data structure called a [**Primitive Unified Bloc (PUB)**](/docs/guides/primitive-input-output#pubs). They are used as inputs to the [`run()`](/docs/api/qiskit-ibm-runtime/estimator-v2#run) method for the Estimator primitive, which executes 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 primitive.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d27b242b-5865-48cc-9052-577982a0b922",
      "metadata": {},
      "source": [
        "## Inputs\n",
        "\n",
        "Each PUB is in this format:\n",
        "\n",
        "(`<single circuit>`, `<one or more observables>`, `<optional one or more parameter values>`, `<optional precision>`),\n",
        "\n",
        "The optional `parameter values` can be a list or a single parameter. Elements from observables and parameter values are combined by following NumPy broadcasting rules as described in the [Primitive inputs and outputs](primitive-input-output#broadcasting-rules) topic, and one expectation value estimate is returned for each element of the broadcasted shape.\n",
        "\n",
        "<Admonition types=\"note\">\n",
        "  If the input contains measurements, they are ignored.\n",
        "</Admonition>\n",
        "\n",
        "For the Estimator primitive, a PUB can 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\" title=\"Commuting observables\">\n",
        "    * Commuting observables **in the same PUB** are grouped together by using [this method](/docs/api/qiskit/qiskit.quantum_info.PauliList#group_qubit_wise_commuting).\n",
        "    * Commuting observables in different PUBs, even if they have the same circuit, are not estimated by using the same measurement. Each PUB represents a different basis for measurement, and therefore, separate measurements are required for each PUB.\n",
        "    * To ensure that commuting observables are estimated by using the same measurement, group them 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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6b4da5a2-093a-4069-99ba-11e2f9c1f9a1",
      "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": "f4b2c3b6-80bd-4d84-9b16-8bdb7502e06b",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import (\n",
        "    Parameter,\n",
        "    QuantumCircuit,\n",
        ")\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    EstimatorV2 as Estimator,\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": "246bd44d-8f3d-40b6-b1c1-795cc3533b32",
      "metadata": {},
      "source": [
        "## Outputs\n",
        "\n",
        "After 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.\n",
        "\n",
        "The `PrimitiveResult` contains an iterable list of [`PubResult`](/docs/api/qiskit/qiskit.primitives.PubResult) objects that contain the execution results for each PUB.\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 `PubResult` objects, one corresponding to each PUB).\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",
        "Each `PubResult` object possesses both a `data` and a `metadata` attribute.\n",
        "\n",
        "* The `data` attribute is a customized [`DataBin`](/docs/api/qiskit/qiskit.primitives.DataBin) that contains the actual measurement values, standard deviations, and so forth.\n",
        "* The `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](/docs/guides/error-mitigation-and-suppression-techniques#zero-noise-extrapolation-zne) or [PEC](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec)).\n",
        "* 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 for the Estimator output:\n",
        "\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",
        "\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",
        "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": "defbebd5-f09f-4596-aff0-ae88cbf2555c",
      "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.01951024  0.16823039  0.31511097 ... -0.2887414  -0.14410851\n",
            "   0.01993466]\n",
            " [ 0.02517623  0.09566969  0.15407855 ... -0.08106747 -0.02517623\n",
            "   0.0448137 ]\n",
            " [ 0.01384425  0.2407911   0.47614339 ... -0.49641533 -0.26304079\n",
            "  -0.00494438]]\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",
        "with np.printoptions(threshold=200):\n",
        "    print(\n",
        "        f\"The expectation values measured from this PUB are: \\n{result[0].data.evs}\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33af7b71-0562-4bb6-8098-8ddebb206032",
      "metadata": {},
      "source": [
        "#### How the Estimator primitive 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`), 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": "cc748369-c7db-4d3d-a2a6-7973fa5c37fe",
      "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": 4,
      "id": "b740caa1-bbdb-421c-aae8-e389a8e55aa1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The metadata of the PrimitiveResult is:\n",
            "'dynamical_decoupling' : {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'},\n",
            "'twirling' : {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'},\n",
            "'resilience' : {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False},\n",
            "'version' : 2,\n",
            "\n",
            "The metadata of the PubResult result is:\n",
            "'shots' : 4096,\n",
            "'target_precision' : 0.015625,\n",
            "'circuit_metadata' : {},\n",
            "'resilience' : {},\n",
            "'num_randomizations' : 32,\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",
      "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
}