diff --git a/docs/guides/_toc.json b/docs/guides/_toc.json index 92088b4badd..8af74660f2d 100644 --- a/docs/guides/_toc.json +++ b/docs/guides/_toc.json @@ -493,12 +493,7 @@ { "title": "Introduction to primitives", "url": "/docs/guides/qiskit-runtime-primitives" - }, - { - "title": "Directed execution model (beta)", - "url": "/docs/guides/directed-execution-model" - }, - { + }, { "title": "Introduction to options", "url": "/docs/guides/runtime-options-overview" }, @@ -560,6 +555,32 @@ } ] }, + { + "title": "Executor", + "isNew": true, + "isNewDate": "2026-05-15", + "children": [ + { + "title": "Get started", + "url": "/docs/guides/get-started-with-executor" + }, + { + "title": "Inputs and outputs", + "url": "/docs/guides/executor-input-output" + }, + { + "title": "Broadcasting", + "url": "/docs/guides/executor-broadcasting" + }, + { + "title": "Options", + "url": "/docs/guides/executor-options" + }, + { + "title": "Examples", + "url": "/docs/guides/executor-examples" + } ] + }, { "title": "Manage noise", "children": [ @@ -577,6 +598,10 @@ } ] }, + { + "title": "Directed execution model (beta)", + "url": "/docs/guides/directed-execution-model" + }, { "title": "Debug Qiskit Runtime jobs", "url": "/docs/guides/debug-qiskit-runtime-jobs" diff --git a/docs/guides/executor-broadcasting.mdx b/docs/guides/executor-broadcasting.mdx new file mode 100644 index 00000000000..77caa11d4e1 --- /dev/null +++ b/docs/guides/executor-broadcasting.mdx @@ -0,0 +1,337 @@ +--- +title: Executor broadcasting +description: How the Executor handles array inputs and outputs using broadcasting semantics + +--- +{/* cspell:ignore broadcastable */} + +# Executor broadcasting + +The data provided to the Executor primitive can be arranged in a variety of shapes to provide flexibility in a workload through broadcasting. This guide explains how the Executor handles array inputs and outputs using broadcasting semantics. Understanding these concepts will help you efficiently sweep over parameter values, combine +multiple configurations, and interpret the shape of returned data. + + +The examples in this topic cannot be run on their own. They assume you have defined appropriate circuits, used the Samplomatic pass manager to add boxes and annotations, and used the Samplomatic `build` method to get a template circuit and samplex for each code block, as necessary. + + + +## Quickstart example + +This example demonstrates the core idea. It creates a parametric circuit and and five different parameter configurations. The executor runs all five configurations and returns data organized by configuration, with one result per classical register in each quantum program item. + +The rest of this guide refers back to this example to explain how this works and how to build more complex sweeps, including Samplomatic-based randomization and inputs. + +```python +import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit +from qiskit_ibm_runtime import QiskitRuntimeService, Executor +from qiskit_ibm_runtime.quantum_program import QuantumProgram +from qiskit.transpiler import generate_preset_pass_manager + +# A circuit with 2 parameters +# This circuit is used throughout the rest of this guide. +circuit = QuantumCircuit(4) +circuit.rx(Parameter("a"), 0) +circuit.rx(Parameter("b"), 1) +circuit.h(2) +circuit.cx(2, 3) +circuit.measure_all() + +# 5 different parameter configurations (shape: 5 configurations × 2 parameters) +parameter_values = np.linspace(0, np.pi, 10).reshape(5, 2) + +# Initialize the service and choose a backend +service = QiskitRuntimeService() +backend = service.least_busy(operational=True, simulator=False) + +# Transpile to ISA circuit +preset_pass_manager = generate_preset_pass_manager( + backend=backend, + optimization_level=3, +) +isa_circuit = preset_pass_manager.run(circuit) + +# This program is used throughout the rest of this guide. +program = QuantumProgram(shots=1024) +program.append_circuit_item(isa_circuit, circuit_arguments=parameter_values) + +# initialize an Executor with default options +executor = Executor(mode=backend) + +# Run and get results +result = executor.run(program).result() + +# result is a list with one entry per program item +# result[0] is a dict mapping classical register names to data arrays +# Output bool arrays have shape (5, 1024, 4) +# 5 = number of parameter configurations +# 1024 = number of shots +# 4 = bits in the classical register +result[0]["meas"] +``` + +## Intrinsic and extrinsic axes + +Broadcasting only applies to _extrinsic_ axes. The intrinsic axes are always preserved as specified. + +- **Intrinsic axes** (rightmost): Determined by the data type. For example, if your circuit + has three parameters, then parameter values require three numbers, giving an intrinsic shape + of `(3,)`. + +- **Extrinsic axes** (leftmost): Your sweep dimensions. These define how many configurations you + want to run. + +| Input type | Intrinsic shape | Example full shape | +|----------------------------------------|---------------|---------------------------------------------------| +| Parameter values (n parameters) | (n,) | (5, 3) for five configurations and three parameters | +| Scalar inputs (for example, noise scale) | () | (4,) for four configurations | +| Observables (if applicable) | varies | Depends on observable type | + +### Example + +Consider a circuit with two parameters that you want to sweep over a 4x3 grid of configurations, varying parameter values and a noise scale factor: + +```python +import numpy as np + +# Parameter values: 4 configurations along axis 0, intrinsic shape (2,) +# Full shape: (4, 1, 2) - the "1" allows broadcasting with noise_scale +parameter_values = np.array([ + [[0.1, 0.2]], + [[0.3, 0.4]], + [[0.5, 0.6]], + [[0.7, 0.8]], +]) # shape (4, 1, 2) + +# Noise scale: 3 configurations, intrinsic shape () (scalar) +# Full shape: (3,) +noise_scale = np.array([0.8, 1.0, 1.2]) # shape (3,) + +# Extrinsic shapes: (4, 1) and (3,) → broadcast to (4, 3) +# Result: 12 total configurations in a 4×3 grid +program.append_samplex_item( + template_circuit, + samplex=samplex, + samplex_arguments={ + "parameter_values": parameter_values, + "noise_scales.mod_ref1": noise_scale, + }, +) +``` + +The shapes are as follows: + +| Input | Full shape | Extrinsic shape | Intrinsic shape | +|------------------|----------|---------------|---------------| +| `parameter_values` | (4, 1, 2) | (4, 1) | (2,) | +| `noise_scale` | (3,) | (3,) | () | +| Broadcast | None | (4, 3) | None | + +## Output array shapes + +Output arrays follow the same extrinsic/intrinsic pattern: + +- **Extrinsic shape:** Matches the broadcast shape of all inputs +- **Intrinsic shape:** Determined by the output type + +The most common output is bitstring data from measurements, which is +formatted as an array of boolean values: + +| Output type | Intrinsic shape | Description | +|-----------------------|----------------------|--------------------------------| +| Classical register data | (num_shots, creg_size) | Bitstring data from measurements | + +### Example + +If you provide inputs with extrinsic shapes `(4, 1)` and `(3,)`, the broadcast extrinsic +shape is `(4, 3)`. The following code uses a circuit with 1024 shots and a 4-bit classical register (as defined in the [Quickstart](#quickstart) example): + +```python +# Input extrinsic shapes: (4, 1) and (3,) → (4, 3) +# Output for classical register "meas": +# extrinsic: (4, 3) +# intrinsic: (1024, 4) - shots × bits +# full shape: (4, 3, 1024, 4) + +result = executor.run(program).result() +meas_data = result[0]["meas"] # result[0] for first program item +print(meas_data.shape) # (4, 3, 1024, 4) + +# Access a specific configuration +config_2_1 = meas_data[2, 1, :, :] # shape (1024, 4) +``` + +Each configuration runs the full shot count specified in the quantum program. Shots are **not** divided among configurations. For example, if you request 1024 shots and have 10 configurations, each configuration runs 1024 shots (10,240 total shots executed). + + +## Randomization and the `shape` parameter + +When using a [samplex](/docs/guides/directed-execution-model), each element of the extrinsic shape corresponds to an independent circuit +execution. The samplex typically injects randomness (for example, gate twirling) into each +execution, so even without explicitly requesting multiple randomizations, each element +receives a random realization. + +You can use the `shape` parameter to augment the extrinsic shape for the item, effectively +adding axes that correspond specifically to randomizing the same configuration many times. It must be +broadcastable from the shape implicit in your `samplex_arguments`. Axes where `shape` exceeds +the implicit shape enumerate additional independent randomizations. + +### No explicit randomization axes + +If you omit `shape` (or set it to match your input shapes), you get one execution per +input configuration. Each execution is still randomized by the samplex, but with only a +single random realization you don't benefit from averaging over multiple randomizations. + + +If you're accustomed to enabling twirling with a simple flag like `twirling=True`, note that +the Executor requires you to explicitly request multiple randomizations with the `shape` argument to +allow your post-processing routines to get the benefits of averaging over multiple +randomizations. A single randomization (the default when `shape` is omitted) applies +random gates but typically offers no advantage over running the base circuit without +randomization. + + +The following example demonstrates the default behavior: + +```python +program.append_samplex_item( + template_circuit, + samplex=samplex, + samplex_arguments={ + "parameter_values": np.random.rand(10, 2), # extrinsic (10,) + }, + # shape defaults to (10,) - one randomized execution per config +) +# Output shape for "meas": (10, num_shots, creg_size) +``` + +### Single randomization axis + +To run multiple randomizations per configuration, extend the shape with additional axes. +For example, the following code runs 20 randomizations for each of 10 parameter configurations: + +```python +program.append_samplex_item( + template_circuit, + samplex=samplex, + samplex_arguments={ + "parameter_values": np.random.rand(10, 2), # extrinsic (10,) + }, + shape=(20, 10), # 20 randomizations × 10 configurations +) +# Output shape for "meas": (20, 10, num_shots, creg_size) +``` + +### Multiple randomization axes + +You can organize randomizations into a multi-dimensional grid. This is useful for structured +analysis, for example, separating randomizations by type or grouping them for statistical processing. + +Here, the input extrinsic shape `(10,)` broadcasts to the requested shape `(2, 14, 10)`, +with axes 0 and 1 filled by independent randomizations. + +```python +program.append_samplex_item( + template_circuit, + samplex=samplex, + samplex_arguments={ + "parameter_values": np.random.rand(10, 2), # extrinsic (10,) + }, + # 2×14=28 randomizations per configuration, 10 configurations + # Or you could set shape=(28, 10) for the same effect + shape=(2, 14, 10), +) +# Output shape for "meas": (2, 14, 10, num_shots, creg_size) +``` + +### How `shape` and input shapes interact + +The `shape` parameter must be broadcastable *from* your input extrinsic shapes. This means: + +- Input shapes with size-1 dimensions can expand to match `shape`. +- Input shapes must align from the right with `shape`. +- Axes in `shape` that exceed the input dimensions enumerate randomizations. + +Note that `shape` can contain size-1 dimensions +that expand to match input dimensions, as is illustrated in the last row of the following table. + +Examples: + +| Input extrinsic | Shape | Result | +|---------------|----------|-----------------------------------------------------------------| +| (10,) | (10,) | 10 configurations, 1 randomization each | +| (10,) | (5, 10) | 10 configurations, 5 randomizations each | +| (10,) | (2, 3, 10) | 10 configurations, 2×3=6 randomizations each | +| (4, 1) | (4, 5) | 4 configurations, 5 randomizations each | +| (4, 3) | (2, 4, 3) | 4×3=12 configurations, 2 randomizations each | +| (4, 3) | (2, 1, 3) | 4×3=12 configurations, 2 randomizations each (the 1 expands to 4) | + + + +### Index into results + + +With randomization axes, you can index into specific randomization/parameter combinations: + +```python +# Using shape=(2, 14, 10) with input extrinsic shape (10,), and +# 1024 shots and 4 classical registers. +result = executor.run(program).result() +meas_data = result[0]["meas"] # shape (2, 14, 10, 1024, 4) + +# Get all shots for randomization (0, 7) and parameter config 3 +specific = meas_data[0, 7, 3, :, :] # shape (1024, 4) + +# Average over all randomizations for parameter config 5 on bit 2 +averaged = meas_data[:, :, 5, :, 2].mean(axis=(0, 1)) +``` + +## Common patterns + +### Sweep a single parameter +Use code like the following to sweep one parameter while holding others fixed: + +```python +# Circuit has 2 parameters, sweep first one over 20 values +sweep_values = np.linspace(0, 2*np.pi, 20) + +parameter_values = np.column_stack([ + sweep_values, + np.full(20, 0.5), +]) # shape (20, 2) +``` + +### Creating a 2D grid sweep +To create a grid over three parameters: + +```python +# Sweep param 0 over 10 values, param 1 over 8 values, param 2 fixed +p0 = np.linspace(0, np.pi, 10)[:, np.newaxis, np.newaxis] # (10, 1, 1) +p1 = np.linspace(0, np.pi, 8)[np.newaxis, :, np.newaxis] # (1, 8, 1) +p2 = np.array([[[0.5]]]) # (1, 1, 1) + +parameter_values = np.broadcast_arrays(p0, p1, p2) +parameter_values = np.stack(parameter_values, axis=-1).squeeze() # (10, 8, 3) + +# Extrinsic shape: (10, 8), intrinsic shape: (3,) +``` + +### Combining multiple inputs + +When combining inputs with different intrinsic shapes, align extrinsic dimensions using +size-1 axes: + +```python +# 4 parameter configurations, 3 noise scales → 4×3 = 12 total configurations +parameter_values = np.random.rand(4, 1, 2) # extrinsic (4, 1), intrinsic (2,) +noise_scale = np.array([0.8, 1.0, 1.2]) # extrinsic (3,), intrinsic () + +# Broadcasted extrinsic shape: (4, 3) +``` + +## Next steps + + + - Review the [broadcasting](/docs/guides/primitive-input-output#broadcasting) overview. + - Understand [Executor inputs and outputs](/docs/guides/executor-input-output). + diff --git a/docs/guides/executor-examples.ipynb b/docs/guides/executor-examples.ipynb new file mode 100644 index 00000000000..0f0f583dbca --- /dev/null +++ b/docs/guides/executor-examples.ipynb @@ -0,0 +1,833 @@ +{ + "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", + "\n", + "# Executor examples" + ] + }, + { + "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", + "
\n", + "Package versions\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", + "
" + ] + }, + { + "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 [Get started with Executor](/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", + "```" + ] + }, + { + "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", + "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." + ] + }, + { + "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)." + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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", + "- 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" + ] + }, + { + "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" + ] + }, + { + "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." + ] + }, + { + "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", + "- 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", + "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", + "\n", + "The usage for this example is approximately 10 minutes on a Heron r2 processor.\n", + "\n", + "\n", + "### Set up the circuit\n", + "\n", + "Choose a backend and prepare a 10-qubit circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f9e93b2c-154a-4d09-872d-f770bcc669c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f8ac3f75-88ca-40f9-8382-6a427303bb8e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "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:" + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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", + "- 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." + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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." + ] + }, + { + "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)." + ] + }, + { + "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)." + ] + }, + { + "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." + ] + }, + { + "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`)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a24c1dd9-7f29-40b0-87ad-25c2a03e0431", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Qubit 0 -> 0.83\n", + "Qubit 1 -> 0.79\n", + "Qubit 2 -> 0.71\n", + "Qubit 3 -> 0.71\n", + "Qubit 4 -> 0.65\n", + "Qubit 5 -> 0.61\n", + "Qubit 6 -> 0.62\n", + "Qubit 7 -> 0.65\n", + "Qubit 8 -> 0.66\n", + "Qubit 9 -> 0.71\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 -> 1.01\n", + "Qubit 1 -> 1.0\n", + "Qubit 2 -> 0.98\n", + "Qubit 3 -> 0.99\n", + "Qubit 4 -> 1.0\n", + "Qubit 5 -> 1.0\n", + "Qubit 6 -> 0.99\n", + "Qubit 7 -> 0.98\n", + "Qubit 8 -> 0.97\n", + "Qubit 9 -> 0.97\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", + "\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", + "" + ] + } + ], + "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 +} diff --git a/docs/guides/executor-input-output.ipynb b/docs/guides/executor-input-output.ipynb new file mode 100644 index 00000000000..fc098ff3ed9 --- /dev/null +++ b/docs/guides/executor-input-output.ipynb @@ -0,0 +1,368 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ce3d197d-7b14-4c60-8d39-a202146d0663", + "metadata": {}, + "source": [ + "---\n", + "title: Executor inputs and outputs\n", + "description: Understand the inputs and outputs to the Executor primitive.\n", + "---\n", + "\n", + "\n", + "# Executor inputs and outputs" + ] + }, + { + "cell_type": "markdown", + "id": "44219323-9419-471a-86c4-488ef0162420", + "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", + "
\n", + "Package versions\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", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "43c044dc-259d-4759-b6a7-a05bd9d3667f", + "metadata": {}, + "source": [ + "The Executor primitive is part of the [directed execution model](/docs/guides/directed-execution-model), which provides more flexibility when customizing an error mitigation workflow.\n", + "\n", + "The inputs and outputs of the Executor primitive are very different from those of the [Sampler](/docs/guides/sampler-input-output) and [Estimator](/docs/guides/estimator-input-output) primitives. For example, instead of taking a list of PUBs as the input, Executor takes a `QuantumProgram`, which contains a list of `QuantumProgramItem` objects. These container classes give you more flexibility than a PUB, which is a simple tuple data structure.\n", + "\n", + "Executor's output is a `QuantumProgramResult`, which is an iterable and contains one element for each input `QuantumProgramItem`.\n", + "\n", + "\n", + "## Inputs: Quantum programs\n", + "\n", + "As stated previously, the input to an Executor primitive is a [`QuantumProgram`](https://qiskit.github.io/qiskit-ibm-runtime/stubs/QuantumProgram.html#QuantumProgram), which is an iterable of\n", + "[`QuantumProgramItem`](https://qiskit.github.io/qiskit-ibm-runtime/stubs/QuantumProgramItem.html) objects. These objects can be of two types:\n", + "- `CircuitItem`, which typically stores a circuit and its parameter values (if any).\n", + "- `SamplexItem`, which typically stores the following:\n", + " - A template circuit\n", + " - A samplex object, which is used to generate randomized sets of parameters at runtime (for example to perform twirling or inject noise)\n", + " - Arguments for the samplex, which might include parameter values for the original circuit\n", + "\n", + "Each of these items represents a different task for Executor to perform.\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", + "### Example: Create a `QuantumProgram` with two different tasks\n", + "\n", + "First initialize your quantum program, then append program items to it by using either `append_circuit_item` or `append_samplex_item` (if a samplex is present), as shown in the following examples.\n", + "\n", + "The following cell initializes a `QuantumProgram` and specifies that it should run 1024 shots for every configuration of each item in the program.\n", + "\n", + "\n", + "Unlike with Sampler, a `QuantumProgram` takes only a single shot value. If you want a different shot value, you need a separate `QuantumProgram`, which would be a separate job.\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81fc7c9e-2cc9-416a-b3d5-74f45eab48fc", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.transpiler import generate_preset_pass_manager\n", + "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n", + "from qiskit_ibm_runtime import Executor, QiskitRuntimeService\n", + "from qiskit.circuit import Parameter, QuantumCircuit\n", + "import numpy as np\n", + "from samplomatic import build\n", + "from samplomatic.transpiler import generate_boxing_pass_manager\n", + "\n", + "# Initialize an empty program\n", + "program = QuantumProgram(shots=1024)\n", + "\n", + "# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.\n", + "circuit = QuantumCircuit(3)\n", + "circuit.h(0)\n", + "circuit.cx(0, 1)\n", + "circuit.cx(1, 2)\n", + "circuit.rz(Parameter(\"theta\"), 0)\n", + "circuit.rz(Parameter(\"phi\"), 1)\n", + "\n", + "# `measure_all` adds a 3-bit classical register named \"meas\"\n", + "circuit.measure_all()\n", + "\n", + "# Choose the least busy backend\n", + "service = QiskitRuntimeService()\n", + "backend = service.least_busy(operational=True, simulator=False)\n", + "\n", + "# Generate a preset pass manager\n", + "# This will be used to convert the abstract circuit to an\n", + "# equivalent Instruction Set Architecture (ISA) circuit.\n", + "preset_pass_manager = generate_preset_pass_manager(\n", + " backend=backend, optimization_level=0\n", + ")\n", + "\n", + "# Transpile the circuit\n", + "isa_circuit = preset_pass_manager.run(circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "a1cc6580-3f31-4322-b0ea-f6bcf032a872", + "metadata": {}, + "source": [ + "### Append a `CircuitItem`\n", + "\n", + "Next, append the target circuit, which was transpiled according to the backend's instruction set architecture (ISA), to the `QuantumProgram`. Since this circuit has two parameters, we must also provide the parameter values (10 sets in this example). Running this `CircuitItem` is the first task that the program will perform." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e6f6762f-1627-450a-b74b-96ad4a3b1c9c", + "metadata": {}, + "outputs": [], + "source": [ + "# Append the transpiled circuit and an array\n", + "# containing 10 sets of parameter values to the program\n", + "program.append_circuit_item(\n", + " isa_circuit,\n", + " circuit_arguments=np.random.rand(\n", + " 10, 2\n", + " ), # 10 sets of parameter values and 2 parameters\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "96f9ac43-8ce4-4389-bff4-5bd68b799974", + "metadata": {}, + "source": [ + "### Append a `SamplexItem`\n", + "\n", + "Circuit items are executed without any sort of randomization. On the contrary, samplex items let you specify how to randomize their content. The next cell uses the `generate_boxing_pass_manager()` function to group the circuit's gates and measurements into boxes and add a twirling annotation to each box. It then generates a template circuit and a samplex pair using the `build()` function.\n", + "\n", + "Running this `SamplexItem` is the second task that the program will perform.\n", + "\n", + "See the Samplomatic [API](https://github.com/Qiskit/samplomatic/) documentation for full details about `samplex` and its arguments. See the Samplomatic [Transpiler guide](https://qiskit.github.io/samplomatic/guides/transpiler.html) for information about using the `generate_boxing_pass_manager()` function." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4ac19f46-226b-4b13-b278-1e39e719484b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TensorInterface(<\n", + " - 'parameter_values' : Input parameter values to use during sampling.\n", + ">)\n" + ] + } + ], + "source": [ + "# Transpile the circuit, additionally grouping gates and measurements into annotated boxes\n", + "preset_pass_manager = generate_preset_pass_manager(\n", + " backend=backend, optimization_level=0\n", + ")\n", + "\n", + "# Use the boxing pass manager to group gates\n", + "# and measurements into boxes and add\n", + "# a`Twirl` annotation.\n", + "preset_pass_manager.post_scheduling = generate_boxing_pass_manager(\n", + " # Add gate twirling\n", + " enable_gates=True,\n", + " # Add measurement twirling\n", + " enable_measures=True,\n", + ")\n", + "boxed_circuit = preset_pass_manager.run(circuit)\n", + "\n", + "# Build the template circuit and the samplex. The template circuit has parametric gates\n", + "# without fixed values and the samplex randomly generates the parameter\n", + "# values on the server side at runtime to perform twirling.\n", + "template_circuit, samplex = build(boxed_circuit)\n", + "\n", + "# Determine what arguments are required by the samplex.\n", + "# Input the arguments in samplex_arguments.\n", + "print(samplex.inputs())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0e3b4d3d-12ea-47f2-a605-b64eed03cf95", + "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", + " # the arguments required by the samplex.sample method\n", + " \"parameter_values\": np.random.rand(10, 2),\n", + " },\n", + " shape=(28, 10), # 28 randomizations and 10 sets of parameter values\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "eb850401-3d4f-4fac-8965-fb9ac4827ebd", + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize an Executor with the 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": "541f5d2d-b974-4ae5-81e0-ef2b6e1dbe82", + "metadata": {}, + "source": [ + "## Outputs\n", + "\n", + "Executor's output is a [`QuantumProgramResult`](https://qiskit.github.io/qiskit-ibm-runtime/stubs/QuantumProgramResult.html#QuantumProgramResult), which is an iterable. It contains one entry per input `QuantumProgramItem` in the same order as the input items. Each of these output items is a dictionary where the keys are strings that correspond the classical registers' names in the input circuits (among others), so you no longer need to memorize these names like you did with Sampler output. The dictionary values are of type `np.ndarray`.\n", + "\n", + "The result for the previous example contains these items:\n", + "\n", + "### `CircuitItem` result\n", + "\n", + "The first item contains the results of running the first task (a `CircuitItem`) in the program. It contains a single key, `meas`, which is the name of the classical register in the input circuit. The value of this key maps to an `np.ndarray` of shape `(parameter sets, shots, register bits)`, which is (10, 1024, 3) for the above example.\n", + "\n", + "The following code illustrates how to access this information:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d652d69a-7a4e-4e5c-8469-97cb9ec92010", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Result shape: (10, 1024, 3)\n" + ] + } + ], + "source": [ + "# Access the results of the classical register of task #0, a CircuitItem\n", + "result_0 = result[0][\"meas\"]\n", + "print(f\"Result shape: {result_0.shape}\")" + ] + }, + { + "cell_type": "markdown", + "id": "7185353a-e7a0-4bb2-b605-664184e684ed", + "metadata": {}, + "source": [ + "### `SamplexItem` result\n", + "\n", + "The second item contains the results of running the second task (a `SamplexItem`) in the program. This item contains multiple keys. The `meas` key, which is the name of the input circuit's classical register, maps to that register's array of results. This array has the shape `(randomizations, parameter sets, shots, classical bits)`, or (28, 10, 1024, 3) in this example. Additionally, the output contains a `measurement_flips.meas` key, which is the bit-flip corrections to undo the measurement twirling for the `meas` register. This output shape will be (28, 10, 1, 3) for our example because only one shot is required to perform the bit-flip." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "884af6e2-614a-4b18-83dc-04505ddbc488", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Result shape: (28, 10, 1024, 3)\n", + "Bit-flip corrections shape: (28, 10, 1, 3)\n" + ] + } + ], + "source": [ + "# Access the results of the classical register of task #1\n", + "result_1 = result[1][\"meas\"]\n", + "print(f\"Result shape: {result_1.shape}\")\n", + "\n", + "# Access the bit-flip corrections\n", + "flips_1 = result[1][\"measurement_flips.meas\"]\n", + "print(f\"Bit-flip corrections shape: {flips_1.shape}\")\n", + "\n", + "# Undo the bit flips via classical XOR\n", + "unflipped_result_1 = result_1 ^ flips_1" + ] + }, + { + "cell_type": "markdown", + "id": "bb0c9225-9a52-4d94-b75d-6ae408f4e029", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + "- Explore [examples](/docs/guides/executor-examples) that use Executor.\n", + "- Learn about the [directed execution model](/docs/guides/directed-execution-model).\n", + "- Understand [Executor broadcasting](/docs/guides/executor-broadcasting).\n", + "" + ] + } + ], + "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 +} diff --git a/docs/guides/executor-options.ipynb b/docs/guides/executor-options.ipynb new file mode 100644 index 00000000000..a57bcd97349 --- /dev/null +++ b/docs/guides/executor-options.ipynb @@ -0,0 +1,333 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7acdb263-fde4-4ff3-a13d-c499a2ba77e4", + "metadata": {}, + "source": [ + "---\n", + "title: Specify Executor options\n", + "description: Specify options when building with the Executor primitive.\n", + "---\n", + "\n", + "\n", + "# Specify Executor options" + ] + }, + { + "cell_type": "markdown", + "id": "73e3d6f4", + "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", + "
\n", + "Package versions\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", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "513837ac-8853-4f45-8d8f-1a8297d5ab68", + "metadata": {}, + "source": [ + "You can use options to customize the Executor primitive.\n", + "\n", + "\n", + "\n", + "- You can see the available options and update option values during or after primitive initialization.\n", + "- Use the `update()` method to apply changes to the `options` attribute.\n", + "- The `options` attribute is the `dataclass` Python type. You can use the built-in `asdict` method to convert it to a dictionary.\n", + "\n", + "\n", + "\n", + "## Set Executor options\n", + "\n", + "If an option is specified both during and after primitive initialization, the value set after initializing the primitive is used.\n", + "\n", + "### Primitive initialization\n", + "\n", + "You can pass in an instance of the options class or a dictionary when initializing Executor, which then makes a copy of those options. Thus, changing the original dictionary or options instance doesn't affect the options owned by the primitive.\n", + "\n", + "#### Options class\n", + "\n", + "When creating an instance of the `Executor` class, you can pass in an instance of the options class. Those options are then applied when you use `run()` to perform the calculation. Specify the options in this format: `options.option.sub-option.sub-sub-option = choice`. For example: `options.environment.log_level = INFO`.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "948f4e75-3079-4c18-ba38-ce536b3f99b5", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime import QiskitRuntimeService, Executor\n", + "from qiskit_ibm_runtime.options import ExecutorOptions\n", + "\n", + "service = QiskitRuntimeService()\n", + "backend = service.least_busy(operational=True, simulator=False)\n", + "\n", + "options = ExecutorOptions(\n", + " environment={\"log_level\": \"INFO\"},\n", + " execution={\"init_qubits\": True},\n", + ")\n", + "\n", + "# or use the following instead:\n", + "\n", + "options = ExecutorOptions()\n", + "options.environment.log_level = \"INFO\"\n", + "options.execution.init_qubits = True\n", + "\n", + "executor = Executor(mode=backend, options=options)" + ] + }, + { + "cell_type": "markdown", + "id": "ba2f5146-2781-49da-9fa2-8a22647225d0", + "metadata": {}, + "source": [ + "#### Dictionary\n", + "\n", + "You can specify options as a dictionary when initializing Executor." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf4359c2-c97a-475c-998d-518dc05a445e", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime import QiskitRuntimeService, Executor\n", + "\n", + "service = QiskitRuntimeService()\n", + "backend = service.least_busy(operational=True, simulator=False)\n", + "\n", + "# Setting options during primitive initialization\n", + "executor = Executor(\n", + " backend,\n", + " options={\n", + " \"environment\": {\"log_level\": \"INFO\"},\n", + " \"execution\": {\"init_qubits\": True},\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "b981eda7-bd9b-4d1e-959a-12cfc35fc2a1", + "metadata": {}, + "source": [ + "\n", + "## Available options\n", + "\n", + "The following table documents options from the latest version of `qiskit-ibm-runtime`. To see older option versions, visit the [`qiskit-ibm-runtime` API reference](/docs/api/qiskit-ibm-runtime) and select a previous version.\n", + "\n", + "\n", + "\n", + "[`environment` API documentation](/docs/api/qiskit-ibm-runtime/options-sampler-options#environment)\n", + "\n", + "\n", + "\n", + "List of tags.\n", + "\n", + "**Choices**: None\n", + "\n", + "**Default**: None\n", + " \n", + "\n", + "\n", + "\n", + "**Choices**: DEBUG, INFO, WARNING, ERROR, CRITICAL\n", + "\n", + "**Default**: WARNING\n", + " \n", + "\n", + "\n", + "\n", + "**Choices**: `True`, `False`\n", + "\n", + "**Default**: `False`\n", + " \n", + "\n", + " \n", + "\n", + "**Choices**: Integer number of seconds in the range [1, 10800]\n", + "\n", + "**Default**: 10800 (3 hours)\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + "[`execution` API documentation](/docs/api/qiskit-ibm-runtime/options-sampler-options#execution)\n", + "\n", + "\n", + "Whether to reset the qubits to the ground state for each shot.\n", + "\n", + "**Choices**: `True`, `False`\n", + "\n", + "**Default**: `True`\n", + " \n", + "\n", + "\n", + "\n", + "The delay between a measurement and the subsequent quantum circuit.\n", + "\n", + "**Choices**: Value in the range supplied by `backend.rep_delay_range`\n", + "\n", + "**Default**: Given by `backend.default_rep_delay`\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "**Choices**: `classified`, `kerneled`, `avg_kerneled`\n", + "\n", + "**Default**: `classified`\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "\n", + " \n", + "\n", + "Experimental options, when available.\n", + "\n", + " \n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "867d346a-3382-4e53-9d96-e3be06f5554b", + "metadata": {}, + "source": [ + "\n", + "## Feature compatibility\n", + "\n", + "Certain runtime features cannot be used together in a single job. Click the appropriate tab for a list of features that are incompatible with the selected feature:\n", + "\n", + "\n", + " \n", + " Incompatible with:\n", + " - Gate-folding ZNE\n", + " - PEA\n", + " - PEC\n", + " - Dynamical decoupling\n", + "\n", + " Other notes:\n", + " - Gate twirling can be applied to dynamic circuits, but only to gates not inside conditional blocks. Measurement twirling can only be applied to terminal measurements.\n", + " - Compatible with fractional gates when using `qiskit-ibm-runtime` v0.42.0 or later.\n", + "\n", + " \n", + " \n", + " Incompatible with dynamic circuits.\n", + "\n", + " \n", + "\n", + " \n", + " Incompatible with:\n", + " - Gate twirling\n", + " - PEA\n", + " - PEC\n", + "\n", + " Compatible with dynamic circuits when using `qiskit-ibm-runtime` v0.42.0 or later.\n", + "\n", + " \n", + "\n", + " \n", + " Incompatible with:\n", + " - Dynamic circuits\n", + " - PEA\n", + " - PEC\n", + "\n", + " Might not work when using custom gates.\n", + " \n", + " \n", + " Incompatible with fractional gates or with stretches.\n", + "\n", + " Other notes:\n", + " - Gate twirling can be applied to dynamic circuits, but only to gates not inside conditional blocks. Measurement twirling can only be applied to terminal measurements.\n", + " - Does not work with non-Clifford entanglers.\n", + "\n", + " \n", + "\n", + " \n", + " Incompatible with:\n", + " - Dynamic circuits\n", + " - Fractional gates\n", + " - Gate-folding ZNE\n", + " - PEC\n", + " \n", + "\n", + " \n", + " Incompatible with:\n", + " - Dynamic circuits\n", + " - Fractional gates\n", + " - Gate-folding ZNE\n", + " - PEA\n", + " \n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "9b86001b-718a-4d72-b3b5-4a67eabf0a45", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + " - Review the [ExecutionOptionsV2](/docs/api/qiskit-ibm-runtime/options-execution-options-v2) API documentation.\n", + " - Review the [EnvironmentOptions](/docs/api/qiskit-ibm-runtime/options-environment-options) API documentation.\n", + "" + ] + } + ], + "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 +} diff --git a/docs/guides/get-started-with-executor.ipynb b/docs/guides/get-started-with-executor.ipynb new file mode 100644 index 00000000000..b2dca2e65ba --- /dev/null +++ b/docs/guides/get-started-with-executor.ipynb @@ -0,0 +1,362 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2d6582ed-04c1-44bb-9dd8-042d46bd8a7b", + "metadata": {}, + "source": [ + "---\n", + "title: Get started with the Executor primitive\n", + "description: How to use the Executor primitive in Qiskit Runtime.\n", + "\n", + "---\n", + "\n", + "# Get started with the Executor primitive" + ] + }, + { + "cell_type": "markdown", + "id": "b91b019e-f1d9-4a7f-9c5a-68ead8bf2a6d", + "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", + "
\n", + "Package versions\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", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "e82a5b3c-eed9-41fe-8940-e88838101ca3", + "metadata": {}, + "source": [ + "Similar to the [Sampler](/docs/guides/get-started-with-sampler) primitive, Executor samples output registers from quantum circuit executions, but it does not have any built in error suppression or mitigation. Instead, it's part of the [directed execution model](/docs/guides/directed-execution-model) that provides the ingredients to capture design intents on the client side, and shifts the costly generation of circuit variants to the server side. Executor follows the directives provided in circuit annotations and options, generates and binds parameter values, executes the bound circuits on the hardware, and returns the execution results and metadata. It does not make any implicit decisions for you and gives you full control and transparency.\n", + "\n", + "\n", + "The Qiskit package does not yet have a base class for the Executor primitive.\n", + "\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", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "9112ddbd-7101-4cac-a53f-5ecf492bc8d4", + "metadata": {}, + "source": [ + "## Steps to use the Executor primitive\n", + "\n", + "### 1. Initialize the account\n", + "\n", + "Because Qiskit Runtime is a managed service, you first need to initialize your account. You can then select the QPU you want to use to calculate the expectation value.\n", + "\n", + "Follow the steps in the [Set up your IBM Cloud® account](/docs/guides/cloud-setup) if you don't already have an account." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c0b08867-a635-481e-ac4f-52359382c94d", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime import QiskitRuntimeService, Executor\n", + "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n", + "from qiskit.circuit import QuantumCircuit\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)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ec105ea7-d806-4333-92af-2d7c578c67d9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print(backend)" + ] + }, + { + "cell_type": "markdown", + "id": "89c4ffd9-acde-4b1f-8ada-fc39b7a708ed", + "metadata": {}, + "source": [ + "### 2. Create and transpile a circuit\n", + "\n", + "You need at least one circuit to use the Executor primitive. It can optionally have parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7671b4dd-b031-44b7-bd4a-48dd39fb93f4", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate the circuit\n", + "circuit = QuantumCircuit(2)\n", + "circuit.h(0)\n", + "circuit.h(1)\n", + "circuit.cz(0, 1)\n", + "circuit.h(1)\n", + "\n", + "# Using `measure_all` automatically creates the necessary\n", + "# classical registers.\n", + "circuit.measure_all()" + ] + }, + { + "cell_type": "markdown", + "id": "127d6de9-084e-47d5-82d6-f2197c0bd74c", + "metadata": {}, + "source": [ + "The circuit needs to be transformed to only use instructions supported by the QPU (referred to as *instruction set architecture (ISA)* circuits). Use the transpiler to do this." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7a98fb20-8731-43f3-a81c-fb2bc8cba9db", + "metadata": {}, + "outputs": [], + "source": [ + "# Transpile the circuit\n", + "preset_pass_manager = generate_preset_pass_manager(\n", + " backend=backend, optimization_level=0\n", + ")\n", + "isa_circuit = preset_pass_manager.run(circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "172b3622-adbb-43f6-9cec-b70dc72094e0", + "metadata": {}, + "source": [ + "### 3. Initialize a `QuantumProgram`\n", + "\n", + "Initialize a `QuantumProgram` with your workload. A `QuantumProgram` is made up of `QuantumProgramItems`. Typically, each item consists of a circuit, a set of parameter values, and possibly a `samplex` to randomize the circuit content. For full details, see [Executor inputs and outputs](/docs/guides/executor-input-output).\n", + "\n", + "The following cell initializes a `QuantumProgram` and specifies to perform 25 shots. Next, it appends the transpiled target circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "eb6719cf-c7f1-4265-8602-9a65071a2dd5", + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize an empty program\n", + "program = QuantumProgram(shots=25)\n", + "\n", + "# Append the circuit to the program\n", + "program.append_circuit_item(isa_circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "da542e9b-f143-4651-ab3d-86fb54c066ab", + "metadata": {}, + "source": [ + "### 4. Optional: Group gates and measurements into annotated boxes\n", + "\n", + "Grouping instructions into boxes and annotating them is the primary way to specify your intent. In the following example, we use `generate_boxing_pass_manager` and its twirling parameters to group two-qubit gates and measurements into boxes and apply twirling annotation." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "245a4574-3ce9-4f77-98c8-af32cde8ac01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Generate a boxing pass manager to group gates\n", + "# and measurements into boxes and add\n", + "# a`Twirl` annotation.\n", + "boxes_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 = boxes_pm.run(isa_circuit)\n", + "boxed_circuit.draw(\"mpl\", idle_wires=False)" + ] + }, + { + "cell_type": "markdown", + "id": "2b2fe4a7-2a1c-489e-9063-3084edfc2517", + "metadata": {}, + "source": [ + "### 5. Optional: Build a template circuit and samplex, and add them to the program\n", + "\n", + "Next, use the Samplomatic [build](https://qiskit.github.io/samplomatic/api/auto/samplomatic.build.html#samplomatic.build) method to generate the _template circuit_ and _samplex_ pair. The template circuit is structurally equivalent to the original circuit. However, its single-qubit gates are replaced by parameterized gates in order to implement the prescribed annotations (gate and measurement twirling, in this example). The samplex encodes all the information required to generate randomized parameters for the template circuit.\n", + "\n", + "After generating the template circuit and samplex pair, use the `append_samplex_item` method to add the pair to the program.\n", + "\n", + "See the Samplomatic [API](https://qiskit.github.io/samplomatic/api/index.html) documentation for full details about `samplomatic.samplex.Samplex` and its arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "47bbeef2-2d85-4495-b740-2e64eb9066b7", + "metadata": {}, + "outputs": [], + "source": [ + "# Build the template circuit and the samplex\n", + "template_circuit, samplex = build(boxed_circuit)\n", + "\n", + "# Append the template circuit and samplex as a `samplex_item`\n", + "program.append_samplex_item(\n", + " template_circuit,\n", + " samplex=samplex,\n", + " shape=(num_randomizations := 20,),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3733cce7-15d5-4c04-a1cd-0bf83e94aeaf", + "metadata": {}, + "source": [ + "### 6. Invoke Executor and get results\n", + "\n", + "Run the `QuantumProgram` on an IBM® backend by using the `Executor` primitive with default options. See [Executor options](/docs/guides/executor-options) to learn about the available options." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b767050c-0299-4100-8be4-b73b0587e088", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Initialize an Executor with the default options\n", + "executor = Executor(mode=backend)\n", + "\n", + "# Submit the job\n", + "job = executor.run(program)\n", + "job" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a341681d-0704-4800-af65-e49b7627bb36", + "metadata": {}, + "outputs": [], + "source": [ + "# Retrieve the result\n", + "result = job.result()" + ] + }, + { + "cell_type": "markdown", + "id": "e649245b-633d-4123-ad55-9971b9366433", + "metadata": {}, + "source": [ + "The result is of type [`QuantumProgramResult`](https://qiskit.github.io/qiskit-ibm-runtime/stubs/qiskit_ibm_runtime.quantum_program.QuantumProgramResult.html#qiskit_ibm_runtime.quantum_program.QuantumProgramResult). See [Executor input and output](/docs/guides/executor-input-output) to learn about the result object." + ] + }, + { + "cell_type": "markdown", + "id": "a6e149b1-de4f-41bb-b972-0781f082fa45", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + " - Try some [Executor examples](/docs/guides/executor-examples).\n", + " - Understand [Executor input and output](/docs/guides/executor-input-output).\n", + " - Learn about [Executor broadcasting semantics](/docs/guides/executor-broadcasting).\n", + "" + ] + } + ], + "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 +} diff --git a/docs/guides/hello-world.ipynb b/docs/guides/hello-world.ipynb index 234c0fcdf8e..7a877d07105 100644 --- a/docs/guides/hello-world.ipynb +++ b/docs/guides/hello-world.ipynb @@ -151,7 +151,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "15f00fb9", "metadata": { "tags": [ @@ -162,8 +162,15 @@ "source": [ "# This cell is hidden from users. It hides several unnecessary warnings.\n", "\n", + "import warnings\n", "import logging\n", "\n", + "warnings.filterwarnings(\"ignore\", message=\".*Instance was not set*\")\n", + "warnings.filterwarnings(\"ignore\", message=\".*loading instance*\")\n", + "warnings.filterwarnings(\"ignore\", message=\".*using instance*\")\n", + "\n", + "# This cell is hidden from users. It hides several unnecessary warnings.\n", + "\n", "\n", "class IgnoreSpecificMessages(logging.Filter):\n", " def filter(self, record):\n", @@ -300,6 +307,18 @@ "id": "9a901271", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "qiskit_runtime_service.__init__:WARNING:2026-02-12 15:13:28,878: Instance was not set at service instantiation. Free and trial plan instances will be prioritized. Based on the following filters: (tags: None, region: us-east, eu-de), and available plans: (internal, premium), the available account instances are: Documentation premium fleet, Documentation internal fleet, Documentation premium fleet, Documentation internal fleet. If you need a specific instance set it explicitly either by using a saved account with a saved default instance or passing it in directly to QiskitRuntimeService().\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:13:30,477: Loading instance: Documentation premium fleet, plan: premium\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:13:32,148: Loading instance: Documentation internal fleet, plan: internal\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:13:32,814: Loading instance: Documentation premium fleet, plan: premium\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:13:35,878: Loading instance: Documentation internal fleet, plan: internal\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:13:43,207: Using instance: Documentation premium fleet, plan: premium\n" + ] + }, { "data": { "text/plain": [ @@ -606,7 +625,20 @@ "execution_count": 12, "id": "428f05e7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "qiskit_runtime_service.__init__:WARNING:2026-02-12 15:14:20,967: Instance was not set at service instantiation. Free and trial plan instances will be prioritized. Based on the following filters: (tags: None, region: us-east, eu-de), and available plans: (internal, premium), the available account instances are: Documentation premium fleet, Documentation internal fleet, Documentation premium fleet, Documentation internal fleet. If you need a specific instance set it explicitly either by using a saved account with a saved default instance or passing it in directly to QiskitRuntimeService().\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:14:22,654: Loading instance: Documentation premium fleet, plan: premium\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:14:24,066: Loading instance: Documentation internal fleet, plan: internal\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:14:24,842: Loading instance: Documentation premium fleet, plan: premium\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:14:27,405: Loading instance: Documentation internal fleet, plan: internal\n", + "qiskit_runtime_service.backends:WARNING:2026-02-12 15:14:33,485: Using instance: Documentation premium fleet, plan: premium\n" + ] + } + ], "source": [ "service = QiskitRuntimeService()\n", "\n", diff --git a/docs/guides/local-testing-mode.ipynb b/docs/guides/local-testing-mode.ipynb index 135ed9483ce..edb1c94482a 100644 --- a/docs/guides/local-testing-mode.ipynb +++ b/docs/guides/local-testing-mode.ipynb @@ -55,6 +55,10 @@ "source": [ "Use local testing mode (available with `qiskit-ibm-runtime` v0.22.0 or later) to test programs before fine-tuning them and sending them to real quantum hardware. After using local testing mode to verify your program, all you need to change is the backend name to run it on a QPU.\n", "\n", + "\n", + "The Executor primitive does not support local testing mode.\n", + "\n", + "\n", "To use local testing mode, specify one of the fake backends from ``qiskit_ibm_runtime.fake_provider`` or specify a Qiskit Aer backend when instantiating a Qiskit Runtime primitive or a session.\n", "\n", "- **Fake backends**: The [fake backends](/docs/api/qiskit-ibm-runtime/fake-provider) in ``qiskit_ibm_runtime.fake_provider`` mimic the behaviors of IBM® QPUs by using QPU snapshots. The QPU snapshots contain important information about the QPU, such as the coupling map, basis gates, and qubit properties, which are useful for testing the transpiler and performing noisy simulations of the QPU. The noise model from the snapshot is automatically applied during simulation.\n", diff --git a/docs/guides/max-execution-time.ipynb b/docs/guides/max-execution-time.ipynb index a595ac1aebe..479521e3709 100644 --- a/docs/guides/max-execution-time.ipynb +++ b/docs/guides/max-execution-time.ipynb @@ -46,7 +46,7 @@ "\n", "The `max_execution_time` value is based on _quantum time_, not wall clock time. Quantum time is the amount of time that the QPU is dedicated to processing your job.\n", "\n", - "Set the maximum execution time (in seconds) on the job options, as shown in the following example. See the [Estimator options](/docs/guides/estimator-options) or [Sampler options](/docs/guides/sampler-options) guides for information about setting options.\n", + "Set the maximum execution time (in seconds) on the job options, as shown in the following example. See the [Estimator options](/docs/guides/estimator-options), [Sampler options](/docs/guides/sampler-options), or [Executor options](/docs/guides/executor-options) guides for information about setting options.\n", "\n", "```python\n", "from qiskit_ibm_runtime import QiskitRuntimeService\n", diff --git a/docs/guides/qiskit-runtime-primitives.mdx b/docs/guides/qiskit-runtime-primitives.mdx index 41af1832708..1a2ba6ee019 100644 --- a/docs/guides/qiskit-runtime-primitives.mdx +++ b/docs/guides/qiskit-runtime-primitives.mdx @@ -26,15 +26,22 @@ The newest Qiskit Runtime primitive, [Executor](https://qiskit.github.io/qiskit- linkText="Get started with Sampler" /> + ## Next steps - Learn about the [Qiskit primitives](/docs/guides/primitives) that the Qiskit Runtime primitives are based on. - - Review detailed [Estimator](/docs/guides/estimator-examples) or [Sampler](/docs/guides/sampler-examples) examples. - - See the input and output details for [Estimator](/docs/guides/estimator-input-output) or [Sampler](/docs/guides/sampler-input-output). + - Review detailed [Estimator](/docs/guides/estimator-examples), [Sampler](/docs/guides/sampler-examples), or [Executor](/docs/guides/executor-examples) examples. + - See the input and output details for [Estimator](/docs/guides/estimator-input-output), [Executor](/docs/guides/executor-input-output), and [Sampler](/docs/guides/sampler-input-output). - Practice with primitives by working through the [Cost function lesson](/learning/courses/variational-algorithm-design/cost-functions) in IBM Quantum Learning. - - See the [EstimatorV2 API reference](/docs/api/qiskit-ibm-runtime/estimator-v2) and [SamplerV2 API reference](/docs/api/qiskit-ibm-runtime/sampler-v2). + - See the [EstimatorV2 API reference](/docs/api/qiskit-ibm-runtime/estimator-v2), [SamplerV2 API reference](/docs/api/qiskit-ibm-runtime/sampler-v2), and [Executor API reference](https://qiskit.github.io/qiskit-ibm-runtime/stubs/qiskit_ibm_runtime.Executor.html). - Read [Migrate to V2 primitives](/docs/guides/v2-primitives). \ No newline at end of file diff --git a/docs/guides/run-jobs-batch.ipynb b/docs/guides/run-jobs-batch.ipynb index a03e711575a..677be1d7906 100644 --- a/docs/guides/run-jobs-batch.ipynb +++ b/docs/guides/run-jobs-batch.ipynb @@ -68,6 +68,7 @@ " Batch,\n", " SamplerV2 as Sampler,\n", " EstimatorV2 as Estimator,\n", + " Executor,\n", ")\n", "\n", "\n", @@ -99,6 +100,7 @@ "batch = Batch(backend=backend)\n", "estimator = Estimator(mode=batch)\n", "sampler = Sampler(mode=batch)\n", + "executor = Executor(mode=batch)\n", "# Close the batch because no context manager was used.\n", "batch.close()" ] @@ -124,12 +126,14 @@ " Batch,\n", " SamplerV2 as Sampler,\n", " EstimatorV2 as Estimator,\n", + " Executor,\n", ")\n", "\n", "backend = service.least_busy(operational=True, simulator=False)\n", "with Batch(backend=backend):\n", " estimator = Estimator()\n", - " sampler = Sampler()" + " sampler = Sampler()\n", + " executor = Executor()" ] }, { diff --git a/docs/guides/run-jobs-session.ipynb b/docs/guides/run-jobs-session.ipynb index af894cd2a8d..3a7ebf2879d 100644 --- a/docs/guides/run-jobs-session.ipynb +++ b/docs/guides/run-jobs-session.ipynb @@ -73,6 +73,7 @@ " Session,\n", " SamplerV2 as Sampler,\n", " EstimatorV2 as Estimator,\n", + " Executor,\n", ")\n", "\n", "service = QiskitRuntimeService()" @@ -112,6 +113,7 @@ "session = Session(backend=backend)\n", "estimator = Estimator(mode=session)\n", "sampler = Sampler(mode=session)\n", + "executor = Executor(mode=session)\n", "# Close the session because no context manager was used.\n", "session.close()" ] @@ -141,12 +143,14 @@ " Session,\n", " SamplerV2 as Sampler,\n", " EstimatorV2 as Estimator,\n", + " Executor,\n", ")\n", "\n", "backend = service.least_busy(operational=True, simulator=False)\n", "with Session(backend=backend):\n", " estimator = Estimator()\n", - " sampler = Sampler()" + " sampler = Sampler()\n", + " executor = Executor()" ] }, { diff --git a/docs/guides/runtime-options-overview.mdx b/docs/guides/runtime-options-overview.mdx index e67f82d5322..23d9e582ec1 100644 --- a/docs/guides/runtime-options-overview.mdx +++ b/docs/guides/runtime-options-overview.mdx @@ -123,4 +123,5 @@ estimator.options.update( - Find details about how to configure [error suppression](error-mitigation-and-suppression-techniques) and [error mitigation](error-mitigation-and-suppression-techniques). - Learn more about [Estimator options](/docs/guides/estimator-options). - Learn more about [Sampler options](/docs/guides/sampler-options). + - Learn more about [Executor options](/docs/guides/executor-options). diff --git a/docs/guides/sampler-input-output.ipynb b/docs/guides/sampler-input-output.ipynb index eb1c2bc460a..804334a1a38 100644 --- a/docs/guides/sampler-input-output.ipynb +++ b/docs/guides/sampler-input-output.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "9d019ecf-6831-4719-812c-8d39202b8aeb", + "id": "1fd07edc-2356-49d3-bf35-6e4e1256b61b", "metadata": {}, "source": [ "---\n", @@ -16,7 +16,7 @@ }, { "cell_type": "markdown", - "id": "95ac260c-9d57-4560-b0f8-dd6aa74fe302", + "id": "fcd633c9-366b-440f-8997-7c60692e5a4d", "metadata": { "tags": [ "version-info" @@ -45,7 +45,7 @@ }, { "cell_type": "markdown", - "id": "6a23bfa3-ae07-45d5-b97f-2572fbd307cb", + "id": "672f7038-64e7-42cc-9936-c30a03596791", "metadata": {}, "source": [ "This page gives an overview of the inputs and outputs of the Qiskit Runtime Sampler primitive, which executes workloads on IBM Quantum® compute resources. Sampler lets you efficiently define vectorized workloads by using a data structure known as a [**Primitive Unified Bloc (PUB)**](/docs/guides/primitive-input-output#pubs). They are used as inputs to the [`run()`](/docs/api/qiskit-ibm-runtime/sampler-v2#run) method for the Sampler 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." @@ -53,7 +53,7 @@ }, { "cell_type": "markdown", - "id": "77516dfb-010d-40b4-a1a1-9f55d939b30b", + "id": "0115445a-d695-4806-8a9a-6bcd2451e418", "metadata": {}, "source": [ "## Inputs\n", @@ -73,7 +73,7 @@ }, { "cell_type": "markdown", - "id": "5345d682-d309-4048-a8b5-218ed21d0834", + "id": "f2314472-55e7-4f31-824e-31b18179e18d", "metadata": {}, "source": [ "---\n", @@ -84,7 +84,7 @@ { "cell_type": "code", "execution_count": 1, - "id": "9d9c28de-7de8-415e-a921-9ae6eb24f39e", + "id": "21687f88-51fc-4139-91b0-3fb4542716ca", "metadata": {}, "outputs": [], "source": [ @@ -145,7 +145,7 @@ }, { "cell_type": "markdown", - "id": "ca7d916f-49cf-4b60-a6f7-371d0e766c49", + "id": "9f1a6c43-a00c-421f-b11d-62d3237f1ccb", "metadata": {}, "source": [ "## Outputs\n", @@ -191,7 +191,7 @@ { "cell_type": "code", "execution_count": 2, - "id": "e733bec6-94bd-4a6a-8745-e5682126f6de", + "id": "e42b2fd8-0790-4a38-9082-f2334440e411", "metadata": {}, "outputs": [ { @@ -207,11 +207,15 @@ "The bytes in register `alpha`, shot by shot:\n", "[[ 3 255]\n", " [ 0 0]\n", + " [ 0 0]\n", " [ 3 255]\n", " ...\n", " [ 3 247]\n", " [ 3 255]\n", " [ 3 248]]\n", + " [ 3 247]\n", + " [ 3 255]\n", + " [ 3 248]]\n", "\n" ] } @@ -247,7 +251,7 @@ }, { "cell_type": "markdown", - "id": "75c95ef9-8184-402e-85e3-4c2bd2d4c0ad", + "id": "5926adf1-c7db-425a-a5cf-cb97eb5fa389", "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." @@ -256,7 +260,7 @@ { "cell_type": "code", "execution_count": 3, - "id": "2b791222-c8c9-4c78-9313-8d9ea8a42a5a", + "id": "b4eb01c9-d438-46ca-9057-4e91b7656748", "metadata": {}, "outputs": [ { @@ -275,7 +279,7 @@ }, { "cell_type": "markdown", - "id": "8d39ab75-9d33-4710-a376-f555903893cd", + "id": "e186d055-09e7-421c-8d70-0d27e7d0edaa", "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:" @@ -284,7 +288,7 @@ { "cell_type": "code", "execution_count": 5, - "id": "6c761c22-69a1-4e29-98ad-7d072e776d0e", + "id": "e81d87ce-2fd6-4498-a9a5-f3432209fccd", "metadata": {}, "outputs": [ { @@ -327,7 +331,7 @@ }, { "cell_type": "markdown", - "id": "486b540a-94fd-4831-81b7-8856fb719afe", + "id": "d93dd9f5-cc43-41b0-b350-f545a14d1f38", "metadata": {}, "source": [ "### Use `BitArray` objects for performant post-processing\n", @@ -338,7 +342,7 @@ { "cell_type": "code", "execution_count": 6, - "id": "936f6b46-4038-4fce-aa4e-5ce181617513", + "id": "6465d11b-9e5b-4078-a1be-75229297093a", "metadata": {}, "outputs": [ { @@ -350,19 +354,24 @@ "[[1]\n", " [0]\n", " [1]\n", + " [1]\n", " ...\n", " [1]\n", " [1]\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", " [ 0 60]\n", + "[[ 1 255]\n", + " [ 0 60]\n", " [ 1 254]\n", " ...\n", " [ 1 255]\n", " [ 1 255]\n", + " [ 1 255]\n", " [ 0 0]]\n", "\n", "The shape of `beta` after post-selection is (0, 2).\n", @@ -372,10 +381,12 @@ "The bytes in `beta` after bit-wise slicing:\n", "[[7]\n", " [4]\n", + " [4]\n", " [6]\n", " ...\n", " [7]\n", " [7]\n", + " [7]\n", " [0]]\n", "\n", "The shape of `beta` after shot-wise slicing is (5, 2).\n", @@ -383,22 +394,32 @@ "[[ 1 255]\n", " [ 0 60]\n", " [ 1 254]\n", + "[[ 1 255]\n", + " [ 0 60]\n", + " [ 1 254]\n", " [ 1 255]\n", " [ 1 255]]\n", + " [ 1 255]]\n", "\n", "Exp. val. for observable `SparsePauliOp(['ZZZZZZZZZ'],\n", " coeffs=[1.+0.j])` is: 0.05224609375\n", + " coeffs=[1.+0.j])` is: 0.05224609375\n", "Exp. val. for observable `SparsePauliOp(['IIIIIIIIZ'],\n", " coeffs=[1.+0.j])` is: 0.01953125\n", + " coeffs=[1.+0.j])` is: 0.01953125\n", "\n", "The shape of the merged results is (4096, 2).\n", "The bytes of the merged results:\n", "[[ 3 255]\n", " [ 0 120]\n", " [ 3 253]\n", + "[[ 3 255]\n", + " [ 0 120]\n", + " [ 3 253]\n", " ...\n", " [ 3 255]\n", " [ 3 255]\n", + " [ 3 255]\n", " [ 0 0]]\n", "\n" ] @@ -448,7 +469,7 @@ }, { "cell_type": "markdown", - "id": "e7c93b53-a453-40ff-a442-f27758eac6e0", + "id": "b3765ba4-ffaa-4f51-a657-1fdfb588b849", "metadata": {}, "source": [ "## Result metadata\n", @@ -465,7 +486,7 @@ { "cell_type": "code", "execution_count": 7, - "id": "45bfa414-1435-444e-b410-6700c1061e97", + "id": "9a40d177-917b-484a-a809-44554efcee28", "metadata": {}, "outputs": [ { @@ -474,6 +495,7 @@ "text": [ "The metadata of the PrimitiveResult is:\n", "'execution' : {'execution_spans': ExecutionSpans([DoubleSliceSpan()])},\n", + "'execution' : {'execution_spans': ExecutionSpans([DoubleSliceSpan()])},\n", "'version' : 2,\n", "\n", "The metadata of the PubResult result is:\n", @@ -494,7 +516,7 @@ }, { "cell_type": "markdown", - "id": "baa89686-c0cb-4280-85d9-e52a94a68df6", + "id": "403dd006-33ec-4bd6-ad45-857535857077", "metadata": {}, "source": [ "\n", @@ -514,7 +536,7 @@ { "cell_type": "code", "execution_count": 8, - "id": "1a475a7f-af09-4ffb-b622-b564717d740e", + "id": "480e553c-2e53-40ca-bafd-e350f173cbcc", "metadata": {}, "outputs": [ { @@ -556,7 +578,7 @@ { "cell_type": "code", "execution_count": 9, - "id": "519eca07-bf97-43b9-9352-af6889273faf", + "id": "092fedcc-c4ac-4841-9846-f77b9ec90d2e", "metadata": {}, "outputs": [], "source": [ @@ -575,7 +597,7 @@ }, { "cell_type": "markdown", - "id": "26324222-7131-4907-b5d4-d5e56b8693a5", + "id": "2ba13f59-c804-4e3b-85c9-90f26cb68e92", "metadata": {}, "source": [ "Execution spans can be filtered to include information pertaining to specific PUBs, selected by their indices:" @@ -584,7 +606,7 @@ { "cell_type": "code", "execution_count": 10, - "id": "736930a9-0207-4206-b184-0041e7e9c87d", + "id": "53dec6a3-57b9-41b0-8ebd-28c6257dd10e", "metadata": {}, "outputs": [ { @@ -594,6 +616,7 @@ ] }, "execution_count": 10, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -605,7 +628,7 @@ }, { "cell_type": "markdown", - "id": "1a37bb41-7a88-43bc-9598-12bf510bb6e2", + "id": "4f635472-60a9-4729-9880-5581fb3806f2", "metadata": {}, "source": [ "View global information about the collection of execution spans:" @@ -614,7 +637,7 @@ { "cell_type": "code", "execution_count": 11, - "id": "d3e11e85-21db-4a3a-8d0f-6f33aa91078b", + "id": "ba4aa14d-4182-47bf-93a6-df607fd82594", "metadata": {}, "outputs": [ { @@ -637,7 +660,7 @@ }, { "cell_type": "markdown", - "id": "e84ef94d-1ea7-4078-a69f-58fee118eebd", + "id": "1392fbc0-0792-402c-9568-160313ab76c4", "metadata": {}, "source": [ "Extract and inspect a particular span:" @@ -646,13 +669,15 @@ { "cell_type": "code", "execution_count": 12, - "id": "e9055022-9399-4de1-be12-f6f7ffe234ca", + "id": "077a74ad-2780-4c61-8310-a4821efbfa68", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + " Start of first span: 2026-05-04 19:11:53.043534\n", + " End of first span: 2026-05-04 19:11:54.004774\n", " Start of first span: 2026-05-04 19:11:53.043534\n", " End of first span: 2026-05-04 19:11:54.004774\n", "#shots in first span: 24\n" @@ -668,7 +693,7 @@ }, { "cell_type": "markdown", - "id": "b2490f0e-60ee-4f6d-906d-255803b14e49", + "id": "924e26ed-ece0-42ef-a985-ae1aaea86622", "metadata": {}, "source": [ " \n", diff --git a/docs/guides/save-jobs.ipynb b/docs/guides/save-jobs.ipynb index 395869b390b..4b9a06a722a 100644 --- a/docs/guides/save-jobs.ipynb +++ b/docs/guides/save-jobs.ipynb @@ -105,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "bd1fc5a5-ad29-4805-91e5-fe70cdbc4540", "metadata": { "tags": [ @@ -125,7 +125,7 @@ "# Get ID of most recent successful job for demonstration.\n", "# This will not work if you've never successfully run a job.\n", "successful_job = next(\n", - " j for j in service.jobs(limit=1000) if j.status() == \"DONE\"\n", + " j for j in service.jobs(limit=100) if j.status() == \"DONE\"\n", ")\n", "job_id = successful_job.job_id()\n", "print(job_id)" diff --git a/docs/guides/v2-primitives.mdx b/docs/guides/v2-primitives.mdx index 61572292a82..f843a119e0a 100644 --- a/docs/guides/v2-primitives.mdx +++ b/docs/guides/v2-primitives.mdx @@ -1206,7 +1206,7 @@ print(f" > Quasi-probability distribution job 2: {another_result.quasi_dists}") ## Next steps - - Learn more about setting options in the [Sampler options](/docs/guides/sampler-options) and [Estimator options](/docs/guides/estimator-options) guides. + - Learn more about setting options in the [Sampler options](/docs/guides/sampler-options), [Estimator options](/docs/guides/estimator-options), and [Executor options](/docs/guides/executor-options) guides. - Learn more details about [Primitive inputs and outputs](/docs/guides/primitive-input-output#pubs). - - Experiment with the [CHSH Inequality](/docs/tutorials/chsh-inequality) tutorial. + - Experiment with the [CHSH inequality](/docs/tutorials/chsh-inequality) tutorial. diff --git a/docs/tutorials/shors-algorithm.ipynb b/docs/tutorials/shors-algorithm.ipynb index 28be0eb1738..d4273ccb7f3 100644 --- a/docs/tutorials/shors-algorithm.ipynb +++ b/docs/tutorials/shors-algorithm.ipynb @@ -33,7 +33,7 @@ "\n", "## Background\n", "\n", - "[Shor's algorithm,](https://epubs.siam.org/doi/abs/10.1137/S0036144598347011) developed by Peter Shor in 1994, is a groundbreaking quantum algorithm for factoring integers in polynomial time. Its significance lies in its ability to factor large integers exponentially faster than any known classical algorithm, threatening the security of widely used cryptographic systems like RSA, which rely on the difficulty of factoring large numbers. By efficiently solving this problem on a sufficiently powerful quantum computer, Shor's algorithm could revolutionize fields such as cryptography, cybersecurity, and computational mathematics, underscoring the transformative power of quantum computation.\n", + "[Shor's algorithm](https://epubs.siam.org/doi/abs/10.1137/S0036144598347011), developed by Peter Shor in 1994, is a groundbreaking quantum algorithm for factoring integers in polynomial time. Its significance lies in its ability to factor large integers exponentially faster than any known classical algorithm, threatening the security of widely used cryptographic systems like RSA, which rely on the difficulty of factoring large numbers. By efficiently solving this problem on a sufficiently powerful quantum computer, Shor's algorithm could revolutionize fields such as cryptography, cybersecurity, and computational mathematics, underscoring the transformative power of quantum computation.\n", "\n", "\n", "This tutorial focuses on demonstrating Shor's algorithm by factoring 15 on a quantum computer.\n", diff --git a/public/docs/images/guides/executor-examples/extracted-outputs/f8ac3f75-88ca-40f9-8382-6a427303bb8e-0.avif b/public/docs/images/guides/executor-examples/extracted-outputs/f8ac3f75-88ca-40f9-8382-6a427303bb8e-0.avif new file mode 100644 index 00000000000..406038c1e4b Binary files /dev/null and b/public/docs/images/guides/executor-examples/extracted-outputs/f8ac3f75-88ca-40f9-8382-6a427303bb8e-0.avif differ diff --git a/public/docs/images/guides/executor-examples/extracted-outputs/f9e93b2c-154a-4d09-872d-f770bcc669c4-0.avif b/public/docs/images/guides/executor-examples/extracted-outputs/f9e93b2c-154a-4d09-872d-f770bcc669c4-0.avif new file mode 100644 index 00000000000..3088137a87f Binary files /dev/null and b/public/docs/images/guides/executor-examples/extracted-outputs/f9e93b2c-154a-4d09-872d-f770bcc669c4-0.avif differ diff --git a/public/docs/images/guides/get-started-with-executor/extracted-outputs/245a4574-3ce9-4f77-98c8-af32cde8ac01-0.avif b/public/docs/images/guides/get-started-with-executor/extracted-outputs/245a4574-3ce9-4f77-98c8-af32cde8ac01-0.avif new file mode 100644 index 00000000000..5620da03cd4 Binary files /dev/null and b/public/docs/images/guides/get-started-with-executor/extracted-outputs/245a4574-3ce9-4f77-98c8-af32cde8ac01-0.avif differ diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml index 27335a9e816..e8512e14205 100644 --- a/qiskit_bot.yaml +++ b/qiskit_bot.yaml @@ -230,6 +230,15 @@ notifications: - "abbycross" - "@jyu00" - "`@beckykd`" + + "docs/guides/executor-options": + - "abbycross" + - "@jyu00" + - "@beckykd" + "docs/guides/get-started-with-executor": + - "abbycross" + - "@jyu00" + - "@beckykd" "docs/guides/get-started-with-backend-primitives": - "abbycross" - "@jyu00" @@ -254,6 +263,14 @@ notifications: - "@abbycross" - "@beckykd" - "@jyu00" + "docs/guides/executor-examples": + - "@abbycross" + - "@beckykd" + - "@jyu00" + "docs/guides/executor-input-output": + - "@abbycross" + - "@beckykd" + - "@jyu00" "docs/guides/get-started-with-sampler": - "@abbycross" - "@beckykd" @@ -523,6 +540,12 @@ notifications: - "@jyu00" - "@ElePT" - "@beckykd" + "docs/guides/executor-broadcasting": + - "@born-2learn" + - "@HuangJunye" + - "@pacomf" + - "@Bagherpoor" + - "@kaelynj" "docs/guides/sampler-rest-api": - "@born-2learn" - "@HuangJunye" diff --git a/scripts/config/allowLists.ts b/scripts/config/allowLists.ts index f164934bc92..71fdb7f211b 100644 --- a/scripts/config/allowLists.ts +++ b/scripts/config/allowLists.ts @@ -27,8 +27,13 @@ const IGNORE_TITLE_MISMATCHES: string[] = [ "docs/guides/estimator-input-output.ipynb", "docs/guides/estimator-options.ipynb", "docs/guides/estimator-rest-api.ipynb", + "docs/guides/executor-examples.ipynb", + "docs/guides/executor-options.ipynb", + "docs/guides/executor-input-output.mdx", + "docs/guides/executor-broadcasting.mdx", "docs/guides/estimator-broadcasting.mdx", "docs/guides/broadcasting.mdx", + "docs/guides/executor-rest-api.mdx", "docs/guides/get-started-with-sampler.ipynb", "docs/guides/pubs.ipynb", "docs/guides/estimator-noise-management.ipynb", @@ -38,6 +43,7 @@ const IGNORE_TITLE_MISMATCHES: string[] = [ "docs/guides/sampler-input-output.ipynb", "docs/guides/sampler-options.ipynb", "docs/guides/get-started-with-estimator.ipynb", + "docs/guides/get-started-with-executor.ipynb", "docs/guides/external-providers-primitives-v2.mdx", "docs/guides/create-a-provider.mdx", "docs/guides/local-simulators.mdx", diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml index 63a3508e5e1..761f7abca79 100644 --- a/scripts/config/notebook-testing.toml +++ b/scripts/config/notebook-testing.toml @@ -80,13 +80,17 @@ notebooks = [ "docs/guides/ibm-circuit-function.ipynb", "docs/guides/sampler-examples.ipynb", "docs/guides/estimator-examples.ipynb", + "docs/guides/executor-examples.ipynb", + "docs/guides/executor-input-output.ipynb", "docs/guides/execute-dynamic-circuits.ipynb", "docs/guides/estimator-rest-api.ipynb", "docs/guides/get-started-with-sampler.ipynb", + "docs/guides/get-started-with-executor.ipynb", "docs/guides/sampler-input-output.ipynb", "docs/guides/sampler-options.ipynb", "docs/guides/estimator-input-output.ipynb", "docs/guides/estimator-options.ipynb", + "docs/guides/executor-options.ipynb", "docs/guides/get-started-with-estimator.ipynb", "docs/guides/qiskit-addons-sqd-get-started.ipynb", "docs/guides/multiverse-computing-singularity.ipynb", @@ -165,6 +169,7 @@ notebooks = [ # We never run tutorials notebooks # "docs/tutorials/implicit-solvent-calculations.ipynb", "docs/tutorials/simulate-kicked-ising-tem.ipynb", + "docs/tutorials/implicit-solvent-calculations.ipynb", "docs/tutorials/dc-hex-ising.ipynb", "docs/tutorials/ghz-spacetime-codes.ipynb", "docs/tutorials/sml-classification.ipynb", diff --git a/scripts/nb-tester/requirements.txt b/scripts/nb-tester/requirements.txt index e0a10367b97..4c688e3e261 100644 --- a/scripts/nb-tester/requirements.txt +++ b/scripts/nb-tester/requirements.txt @@ -14,6 +14,7 @@ qiskit-addon-aqc-tensor[aer,quimb-jax]~=0.2.0; sys.platform != 'darwin' qiskit-addon-obp~=0.3.0 qiskit-addon-cutting~=0.10.0 qiskit-experiments~=0.13.0 +samplomatic~=0.18.0 scipy~=1.17.1 scikit-learn~=1.8.0 pyscf~=2.12.1; sys.platform != 'win32'