diff --git a/docs/guides/_toc.json b/docs/guides/_toc.json index b8c410828e..f07ff1801f 100644 --- a/docs/guides/_toc.json +++ b/docs/guides/_toc.json @@ -299,6 +299,10 @@ { "title": "Primitives with REST API", "url": "/guides/primitives-rest-api" + }, + { + "title": "Noise learning", + "url": "/guides/noise-learning" } ] }, diff --git a/docs/guides/execute-on-hardware.mdx b/docs/guides/execute-on-hardware.mdx index c13cfdbb77..a2a69648ea 100644 --- a/docs/guides/execute-on-hardware.mdx +++ b/docs/guides/execute-on-hardware.mdx @@ -31,6 +31,7 @@ expectation values of observables corresponding to physical quantities or cost f * [PUBs and primitive results](./primitive-input-output) * [Primitives examples](./primitives-examples) * [Primitives with REST API](./primitives-rest-api) +* [Noise learning helper](./noise-learning) ### Configure runtime options * [Overview](./runtime-options-overview) diff --git a/docs/guides/noise-learning.ipynb b/docs/guides/noise-learning.ipynb new file mode 100644 index 0000000000..e6c02dde72 --- /dev/null +++ b/docs/guides/noise-learning.ipynb @@ -0,0 +1,303 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1b2370f0-a4ee-49b1-9e53-e4527d37bc6f", + "metadata": {}, + "source": [ + "# Noise learning helper\n", + "\n", + "The error mitigation techniques [PEA](./error-mitigation-and-suppression-techniques#probabilistic-error-amplification-pea) and [PEC](./error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec) both utilize a noise learning component based on a [Pauli-Lindblad noise model](https://arxiv.org/abs/2201.09866), which is typically managed during execution after submitting one or more jobs through `qiskit-ibm-runtime` without any local access to the fitted noise model. However as of `qiskit-ibm-runtime` 0.27.1, a [`NoiseLearner`](../api/qiskit-ibm-runtime/noise_learner) and associated [`NoiseLearnerOptions`](../api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.NoiseLearnerOptions) class have been created to obtain the results of these noise learning experiments. These results can then be stored locally as a `NoiseLearnerResult` and used as input in later experiments. This page provides an overview of its usage and the associated options available.\n", + "\n", + "\n", + "## Overview\n", + "\n", + "The `NoiseLearner` class performs experiments which characterizes noise processes based on a Pauli-Lindblad noise model for one (or more) circuits. It possesses a `run()` method which will execute the learning experiments and takes as input either a list of circuits or a [PUB](./primitive-input-output#overview-of-pubs) and returns a `NoiseLearnerResult` containing the learned noise channels and metadata about the job(s) submitted. Below is a code snippet demonstrating the usage of the helper program." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5118119-5892-4ad9-9908-107795a1f1fa", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import QuantumCircuit\n", + "from qiskit.transpiler import CouplingMap\n", + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2\n", + "from qiskit_ibm_runtime.noise_learner import NoiseLearner\n", + "from qiskit_ibm_runtime.options import NoiseLearnerOptions, ResilienceOptionsV2, EstimatorOptions\n", + "\n", + "# Build a circuit with two entangling layers\n", + "num_qubits = 27\n", + "edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))\n", + "even_edges = edges[::2]\n", + "odd_edges = edges[1::2]\n", + "\n", + "circuit = QuantumCircuit(num_qubits)\n", + "for pair in even_edges:\n", + " circuit.cx(pair[0], pair[1])\n", + "for pair in odd_edges:\n", + " circuit.cx(pair[0], pair[1])\n", + "\n", + "# Choose a backend to run on\n", + "service = QiskitRuntimeService()\n", + "backend = service.least_busy()\n", + "\n", + "# Transpile the circuit for execution\n", + "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n", + "circuit_to_learn = pm.run(circuit)\n", + "\n", + "# Instantiate a NoiseLearner object and execute the noise learning program\n", + "learner = NoiseLearner(mode=backend)\n", + "job = learner.run([circuit_to_learn])\n", + "noise_model = job.result()" + ] + }, + { + "cell_type": "markdown", + "id": "1859540c-a597-411a-ab98-1b436b284f8a", + "metadata": {}, + "source": [ + "The resulting `NoiseLearnerResult.data` is a list of [`LayerError`](../api/qiskit-ibm-runtime/qiskit_ibm_runtime.utils.noise_learner_result.LayerError) objects containing the [noise model](https://arxiv.org/abs/2201.09866) for each individual entangling layer that belongs to the target circuit(s). Each `LayerError` stores the layer information, in the form of a circuit and a set of qubit labels, alongside the [`PauliLindBladError`](../api/qiskit-ibm-runtime/qiskit_ibm_runtime.utils.noise_learner_result.PauliLindbladError) for the noise model that was learned for the given layer." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "33d8cc11-6c6f-4838-8038-dd9994476db4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Noise learner result contains 2 entries and has the following type:\n", + " \n", + "\n", + "Each element of `NoiseLearnerResult` then contains an object of type:\n", + " \n", + "\n", + "And each of these `LayerError` objects possess the following data: \n", + "PauliLindbladError(generators=['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIXYI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIXZI', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIYXI', 'IIIIIIIIIIIIIIIIIIIIIIIIYYI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIYZI', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIZXI', 'IIIIIIIIIIIIIIIIIIIIIIIIZYI',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIIYIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIXIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIXYIII', 'IIIIIIIIIIIIIIIIIIIIIIXZIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIYIIII', 'IIIIIIIIIIIIIIIIIIIIIIYXIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIYZIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIZXIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIIZYIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIXIIIII', 'IIIIIIIIIIIIIIIIIIIIIXIIXII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIXIIYII', 'IIIIIIIIIIIIIIIIIIIIIXIIZII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIXXIIII', 'IIIIIIIIIIIIIIIIIIIIIXYIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIXZIIII', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIYIIXII', 'IIIIIIIIIIIIIIIIIIIIIYIIYII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIYIIZII', 'IIIIIIIIIIIIIIIIIIIIIYXIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIYZIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIXII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIZIIYII', 'IIIIIIIIIIIIIIIIIIIIIZIIZII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIZXIIII', 'IIIIIIIIIIIIIIIIIIIIIZYIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIXIIXIII', 'IIIIIIIIIIIIIIIIIIIIXIIYIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIXIIZIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIYIIXIII', 'IIIIIIIIIIIIIIIIIIIIYIIYIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIYIIZIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',\n", + " 'IIIIIIIIIIIIIIIIIIIIZIIXIII', 'IIIIIIIIIIIIIIIIIIIIZIIYIII', ...], rates=[0.00067, 0.00083, 0.00523, 0.00044, 0.00011, 2e-05, 7e-05, 0.00031, 0.00011, 2e-05, 7e-05, 0.0, 0.0, 0.0, 0.0, 0.00092, 0.00052, 0.00065, 9e-05, 0.0, 0.0, 0.0, 0.00205, 0.0, 0.0, 0.0, 0.0017, 8e-05, 0.00083, 0.00251, 0.00059, 0.0002, 0.0, 0.0, 0.00064, 0.0, 0.0002, 0.0, 0.0005, 0.0, 0.0, 0.00314, 0.00065, 0.0, 0.0, 0.00012, 0.00083, 0.00079, 0.00027, 0.00053, 0.0, 0.0012, 0.00075, 0.0, 0.0, 0.00194, 0.0, 0.0, 0.00051, 0.00142, 0.0, 5e-05, 0.00248, 0.00204, 0.00102, 0.00027, 4e-05, 0.00122, 2e-05, 0.00027, 0.00044, 0.0, 0.0, 0.0, 0.00175, 0.00083, 0.00087, 0.00116, 0.00081, 0.0, 0.0, 0.0, 0.00104, 0.0, 0.00013, 9e-05, 0.00048, 0.00013, 0.0, 9e-05, 0.00037, 1e-05, 8e-05, 6e-05, 0.00062, 0.0, 0.0, 0.00046, 1e-05, 0.00012, 2e-05, 0.00053, 7e-05, 0.0, 0.00134, 0.0, 0.00041, 0.00035, 0.00018, 0.00071, 0.00126, 0.00087, 0.00089, 0.00073, 0.00093, 0.00123, 0.00122, 0.0, 0.00056, 0.0, 0.0, 0.0009, 0.00077, 0.0002, 0.00013, 0.00068, 0.00068, 0.0, 0.00011, 0.0, 0.00079, 0.0, 0.0, 0.00011, 0.00062, 7e-05, 2e-05, 2e-05, 0.00131, 0.0009, 0.00079, 0.00031, 0.00033, 0.00023, 0.00015, 0.00045, 0.00024, 0.0, 0.00013, 0.00054, 0.00116, 0.0, 0.0, 0.00021, 0.00189, 0.0, 0.00021, 0.0, 0.00188, 0.0, 0.0, 0.0, 0.00076, 0.001, 0.00097, 0.00017, 0.00046, 0.0, 0.0, 0.00064, 0.00056, 0.00013, 0.0, 0.00055, 0.00111, 4e-05, 4e-05, 0.0, 0.00076, 0.0, 0.00031, 0.00017, 0.00188, 0.00031, 0.0, 0.00017, 0.00148, 0.00211, 0.00138, 0.0001, 0.00052, 0.00027, 0.00012, 0.00087, 0.00068, 0.0, 4e-05, 0.00071, 0.00059, 0.0, 2e-05, 1e-05, 0.0007, 0.0, 1e-05, 2e-05, 0.00109, 0.00023, 5e-05, 5e-05, 0.00094, 0.00072, 0.00061, 0.0, 0.00028, 2e-05, 1e-05, 0.00081, 0.00028, 0.0, 0.0, 0.00081, 0.00123, 0.0, 0.00027, 0.00027, 0.00181, 0.00046, 0.00026, 0.0, 0.00706, 0.00046, 0.0, 0.00026, 0.00012, 0.00061, 0.00039, 0.00034, 0.00048, 0.00025, 0.00021, 0.00172, 0.00073, 0.00079, 0.00778, 0.00253, 0.00016, 0.00317, 0.0, 0.00094, 0.0, 0.00013, 0.00037, 0.00267, 0.00066, 0.00046, 0.0, 0.00013, 0.0, 0.00037, 0.0011, 0.00128, 0.00236, 0.00124, 0.0, 0.0, 0.00518, 0.00067, 3e-05, 3e-05, 2e-05, 0.00095, 3e-05, 2e-05, 3e-05, 0.00079, 0.0, 0.00014, 0.00014, 0.00176, 0.00103, 0.00074, 0.00035, 0.0, 0.0, 0.0, 0.00121, 3e-05, 0.00019, 0.00016, 0.00112, 0.00114, 0.0, 0.0, 0.0, 0.0, 0.0, 0.00109, 0.0, 0.0, 0.0, 0.0, 0.00124, 0.0036, 0.00357, 0.0, 0.0, 0.00357, 0.0036, 0.0, 0.0, 0.00066, 0.00052, 0.00093, 0.00079])\n", + "\n" + ] + } + ], + "source": [ + "print(f'Noise learner result contains {len(noise_model.data)} entries'\\\n", + " f' and has the following type:\\n {type(noise_model)}\\n')\n", + "print(f'Each element of `NoiseLearnerResult` then contains'\\\n", + " f' an object of type:\\n {type(noise_model.data[0])}\\n')\n", + "print(f'And each of these `LayerError` objects possess the'\\\n", + " f' following data: \\n{noise_model.data[0].error}\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "a27a38f1-393d-49f9-92c3-a8634bfb1a60", + "metadata": {}, + "source": [ + "The `LayerError.error` attribute of the noise learning result contains the generators and error rates of the fitted Pauli Lindblad model, which has the form:\n", + "\n", + "$$ \\Lambda(\\rho) = \\exp{\\sum_j r_j \\left(P_j \\rho P_j^\\dagger - \\rho\\right)} $$\n", + "\n", + "where the $r_j$ are the `LayerError.rates` and $P_j$ are the Pauli operators specified in `LayerError.generators`." + ] + }, + { + "cell_type": "markdown", + "id": "7ebb4b7d-ef45-4996-bf5a-125d0831ebca", + "metadata": {}, + "source": [ + "## Noise learning options\n", + "\n", + "There are a few options available to input into a `NoiseLearner` object when instantiated. These are encapsulated by the `qiskit_ibm_runtime.options.NoiseLearnerOptions` class and include the ability to specify the maximum layers to learn, number of randomizations, and the twirling strategy, among others. Refer to the API documentation about [`NoiseLearnerOptions`](../api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.NoiseLearnerOptions) for more detailed information.\n", + "\n", + "Below is a simple example showing how to use the `NoiseLearnerOptions` in a `NoiseLeaner` experiment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f35d68a-b459-4f1c-ae44-a01bbe700ed0", + "metadata": {}, + "outputs": [], + "source": [ + "# Build a GHZ circuit\n", + "circuit = QuantumCircuit(10)\n", + "circuit.h(0)\n", + "circuit.cx(range(0, 9), range(1, 10))\n", + "# Choose a backend to run on\n", + "service = QiskitRuntimeService()\n", + "backend = service.least_busy()\n", + "\n", + "# Transpile the circuit for execution\n", + "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n", + "circuit_to_run = pm.run(circuit_to_learn)\n", + "\n", + "# Instantiate a noise learner options object\n", + "learner_options = NoiseLearnerOptions(\n", + " max_layers_to_learn = 3,\n", + " num_randomizations = 32,\n", + " twirling_strategy = \"all\"\n", + ")\n", + "\n", + "# Instantiate a NoiseLearner object and execute the noise learning program\n", + "learner = NoiseLearner(mode=backend, options=learner_options)\n", + "job = learner.run([circuit_to_run])\n", + "noise_model = job.result()" + ] + }, + { + "cell_type": "markdown", + "id": "694b0e38-7aeb-48e0-8598-b64cff395bc2", + "metadata": {}, + "source": [ + "## Input noise model to primitive\n", + "\n", + "The noise model learned on the circuit can also be used as an input to the `EstimatorV2` primitive implemented in Qiskit IBM Runtime. This can be passed into the primitive a few different ways. For example, you can pass the noise model to the `estimator.options` attribute directly" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "d8b504c1-17ad-4e23-b508-eac1955bd1d6", + "metadata": {}, + "outputs": [], + "source": [ + "estimator = EstimatorV2(mode=backend)\n", + "estimator.options.resilience.layer_noise_model = noise_model" + ] + }, + { + "cell_type": "markdown", + "id": "3892248e-5221-455d-ba74-e75ffb917a0e", + "metadata": {}, + "source": [ + "via a `ResilienceOptionsV2` object before instantiating an Estimator primitive" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "8aa753bc-915d-4d47-ba56-5179ec9cf4ca", + "metadata": {}, + "outputs": [], + "source": [ + "# Specify options via a ResilienceOptionsV2 object\n", + "resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)\n", + "estimator_options = EstimatorOptions(resilience=resilience_options)\n", + "estimator = EstimatorV2(mode=backend, options=estimator_options)" + ] + }, + { + "cell_type": "markdown", + "id": "199d18c8-5eba-44db-a6cf-a9a7a8018734", + "metadata": {}, + "source": [ + "or by passing in an appropriately formatted dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "6dcb70fe-e291-4b5a-a79b-14cace921905", + "metadata": {}, + "outputs": [], + "source": [ + "# Specify options via a dictionary\n", + "options_dict = {'resilience_level':2,\n", + " 'resilience':{'layer_noise_model':result}}\n", + "\n", + "estimator = EstimatorV2(mode=backend, options=options_dict)" + ] + }, + { + "cell_type": "markdown", + "id": "8fc627b2-fee5-4275-9895-a48d65ef26f1", + "metadata": {}, + "source": [ + "Once the noise model is passed into the `EstimatorV2` object, it can be used to run workloads and perform error mitigation as normal." + ] + }, + { + "cell_type": "markdown", + "id": "6bcd1032-a674-466a-b40b-87f0c4d85984", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "\n", + " - Read more about [configuring error mitigation](configure-error-mitigation).\n", + " - Review the [EstimatorOptions API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.EstimatorOptions) and [ResilienceOptionsV2 API reference](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.options.ResilienceOptionsV2).\n", + " - Learn more about [error mitigation and suppression techniques](error-mitigation-and-suppression-techniques) that are available through Qiskit Runtime.\n", + " - Review how to [specify options](specify-runtime-options) for the Qiskit Runtime primitives.\n", + " - Read [Migrate to V2 primitives](/migration-guides/v2-primitives).\n", + "" + ] + } + ], + "metadata": { + "description": "Get started with the noise learning helper program to save the noise models created when executing workloads in Qiskit IBM Runtime", + "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" + }, + "title": "Noise learning helper" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml index 64942e93d6..b6376c183c 100644 --- a/qiskit_bot.yaml +++ b/qiskit_bot.yaml @@ -274,3 +274,5 @@ notifications: - "@johannesgreiner" - "@pacomf" - "@Bagherpoor" + "docs/guides/noise-learning": + - "@kaelynj" diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml index 9a12b3aace..128b3ac328 100644 --- a/scripts/config/notebook-testing.toml +++ b/scripts/config/notebook-testing.toml @@ -47,4 +47,5 @@ notebooks_that_submit_jobs = [ # memory on a reasonable device. notebooks_no_mock = [ "docs/guides/hello-world.ipynb", + "docs/guides/noise-learning.ipynb" ]