diff --git a/docs/addons/qiskit-addon-obp/_toc.json b/docs/addons/qiskit-addon-obp/_toc.json new file mode 100644 index 00000000000..b55fb020f9e --- /dev/null +++ b/docs/addons/qiskit-addon-obp/_toc.json @@ -0,0 +1,71 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Operator backpropagation (OBP) 0.3.0", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation home", + "url": "/docs/addons/qiskit-addon-obp" + }, + { + "title": "Installation instructions", + "url": "/docs/addons/qiskit-addon-obp/install" + }, + { + "title": "Guides", + "children": [ + { + "title": "Overview", + "url": "/docs/addons/qiskit-addon-obp/guides/overview" + }, + { + "title": "Quick start guide", + "url": "/docs/addons/qiskit-addon-obp/guides/quickstart" + }, + { + "title": "Bound error in experiments", + "url": "/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm" + }, + { + "title": "Simulate circuits with OBP", + "url": "/docs/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp" + }, + { + "title": "Truncate operator terms during backpropagation", + "url": "/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-obp" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "collapsible": false, + "children": [ + { + "title": "Operator backpropagation (OBP) for estimation of expectation values", + "url": "https://quantum.cloud.ibm.com/docs/en/tutorials/operator-back-propagation" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Python API reference", + "url": "https://quantum.cloud.ibm.com/docs/en/api/qiskit-addon-obp" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm.ipynb b/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm.ipynb new file mode 100644 index 00000000000..8aca8419b89 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm.ipynb @@ -0,0 +1,470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Use different Lp-norms for Pauli term truncation\"\n", + "description: \"Use different Lp-norms for Pauli term truncation for the latest version of Operator backpropagation (OBP)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "8a7c9df5-d836-4ecb-8aec-8a1f2faa017a", + "metadata": {}, + "source": [ + "# Use different Lp-norms for Pauli term truncation" + ] + }, + { + "cell_type": "markdown", + "id": "5958e635-64c1-4599-82ca-225b83312906", + "metadata": {}, + "source": [ + "**Note:** Before reading this guide, you should read the [Truncate Pauli terms](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) guide, which describes the truncation of low-weight Pauli terms that is built into the [backpropagate](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) method based on a specified [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget)." + ] + }, + { + "cell_type": "markdown", + "id": "2780c587-cd2a-4398-8faa-de5e0c661a10", + "metadata": {}, + "source": [ + "In this guide, you will learn about the [backpropagate](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) keyword argument, `p_norm`, which can be used to change the Lp-norm used to estimate the error incurred by the truncated Pauli terms." + ] + }, + { + "cell_type": "markdown", + "id": "872aeae7-0312-41db-8ebb-f7b02838b54d", + "metadata": {}, + "source": [ + "## Construct an example circuit\n", + "\n", + "This guide uses the same example circuit as in the [Truncate Pauli terms](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) guide:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6f281d4-706e-41ad-b7b5-bc7ebe106996", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import rustworkx.generators\n", + "from qiskit.synthesis import LieTrotter\n", + "from qiskit_addon_utils.problem_generators import (\n", + " PauliOrderStrategy,\n", + " generate_time_evolution_circuit,\n", + " generate_xyz_hamiltonian,\n", + ")\n", + "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n", + "\n", + "# Generate a linear chain of 10 qubits\n", + "num_qubits = 10\n", + "linear_chain = rustworkx.generators.path_graph(num_qubits)\n", + "\n", + "# Use an arbitrary XY model\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " linear_chain,\n", + " coupling_constants=(0.05, 0.02, 0.0),\n", + " ext_magnetic_field=(0.02, 0.08, 0.0),\n", + " pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,\n", + ")\n", + "# Evolve for some time\n", + "circuit = generate_time_evolution_circuit(\n", + " hamiltonian, synthesis=LieTrotter(reps=3), time=2.0\n", + ")\n", + "# slice the circuit by gate type\n", + "slices = slice_by_gate_types(circuit)\n", + "\n", + "# For visualization purposes only, recombine the slices with barriers between them and draw the resulting circuit\n", + "combine_slices(slices, include_barriers=True).draw(\"mpl\", fold=50, scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "7a7c7299-0c82-4279-b3dc-4f39e8dddd7e", + "metadata": {}, + "source": [ + "Define an observable for the total magnetization:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ea9e7adc-b003-4762-a889-10e8d84a63d5", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "obs = SparsePauliOp.from_sparse_list(\n", + " [(\"Z\", [i], 1.0) for i in range(num_qubits)], num_qubits=num_qubits\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "423150d8-80d2-4b80-9665-033808d30272", + "metadata": {}, + "source": [ + "For reference, compute the exact expectation value:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4dc72426-2eb5-4f8d-8bba-79650da06b54", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.318197859862146\n" + ] + } + ], + "source": [ + "from qiskit.primitives import StatevectorEstimator\n", + "\n", + "estimator = StatevectorEstimator()\n", + "job = estimator.run([(circuit, obs)])\n", + "res = job.result()\n", + "exact_exp = res[0].data.evs\n", + "print(exact_exp)" + ] + }, + { + "cell_type": "markdown", + "id": "d21490c0-e86c-4fe0-af90-c9625813e0c0", + "metadata": {}, + "source": [ + "## Use the L1 norm\n", + "\n", + "By default, and as you have already seen in the [Truncate Pauli terms](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) guide, `p_norm=1`, which means that the error is estimated as:\n", + "$$\n", + "|\\langle\\psi|\\Delta|\\psi\\rangle| \\leq \\sum_{P\\in\\mathcal{T}} |c_P|\n", + "$$\n", + "where $\\psi$ is the quantum state, $\\Delta$ is the real difference between the exact and truncated observable (which is unknown), $\\mathcal{T}$ is the set of Pauli terms that were truncated, and $c_P$ is the Pauli terms coefficient.\n", + "This inequality is a rigorous but very loose upper bound for most scenarios." + ] + }, + { + "cell_type": "markdown", + "id": "425293ce-49fb-4bd0-8d2f-44a3a43db967", + "metadata": {}, + "source": [ + "This guide, backpropagates six slices of the example circuit, using a constant error per slice of `0.001`. This value is to be understood as the budget within the given `p_norm`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b46531b3-055b-4112-903a-11f2dd52a9ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n" + ] + } + ], + "source": [ + "from qiskit_addon_obp.utils.truncating import setup_budget\n", + "\n", + "l1_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=1)\n", + "print(l1_truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "bc252678-58a0-407e-91fe-b7c545d57ae8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 6 circuit slices.\n", + "New observable contains 116 terms and 10 commuting groups.\n" + ] + } + ], + "source": [ + "from qiskit_addon_obp import backpropagate\n", + "\n", + "max_slices = 6\n", + "l1_bp_obs, l1_remaining_slices, l1_metadata = backpropagate(\n", + " obs,\n", + " slices[-max_slices:],\n", + " truncation_error_budget=l1_truncation_error_budget,\n", + ")\n", + "l1_reduced_circuit = combine_slices(\n", + " slices[:-max_slices] + l1_remaining_slices\n", + ")\n", + "print(\n", + " f\"Backpropagated {max_slices - len(l1_remaining_slices)} circuit slices.\"\n", + ")\n", + "print(\n", + " f\"New observable contains {len(l1_bp_obs)} terms and {len(l1_bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "9f2782f8-1e93-4319-af26-dc467d7ed459", + "metadata": {}, + "source": [ + "We can now compute the expectation value of the backpropagated observable, as well as the error with respect to the exact reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5be8c457-6569-452d-8502-0fc5e39bf918", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.317869899338842 0.00032796052330397174\n" + ] + } + ], + "source": [ + "estimator = StatevectorEstimator()\n", + "job = estimator.run([(l1_reduced_circuit, l1_bp_obs)])\n", + "res = job.result()\n", + "l1_exp = res[0].data.evs\n", + "l1_error = exact_exp - l1_exp\n", + "print(l1_exp, l1_error)" + ] + }, + { + "cell_type": "markdown", + "id": "c0eccc5b-dc71-45b3-ad79-3d3dcb491f2d", + "metadata": {}, + "source": [ + "Finally, we can plot the error incurred during the backpropagation of each slice, as well as the accumulated error.\n", + "The accumulated error is the sum of the slice errors. We can see that the accumulated error is a very loose upper bound to the actual error." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ff72e77f-e2c0-4cce-8575-c5aa587f78fb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "from qiskit_addon_obp.utils.visualization import (\n", + " plot_accumulated_error,\n", + " plot_slice_errors,\n", + ")\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n", + "axes[1].plot([6], [l1_error], \"x\", color=\"red\", label=\"actual error\")\n", + "plot_slice_errors(l1_metadata, axes[0])\n", + "plot_accumulated_error(l1_metadata, axes[1])" + ] + }, + { + "cell_type": "markdown", + "id": "10dd1139-c597-43c2-9f58-0a0988de768f", + "metadata": {}, + "source": [ + "## Use the L2 norm\n", + "\n", + "One can argue that the L2 norm is a better approximation of the incurred error than the L1 norm.\n", + "That is because we can assume the quantum state $|\\psi\\rangle$ to be drawn from a Haar random ensemble, in which case the incurred error follows a distribution with a vanishing mean and a variance that can be approximately bound by the L2 norm:\n", + "$$\n", + "|\\langle\\psi|\\Delta|\\psi\\rangle| \\lesssim \\left( \\sum_{P\\in\\mathcal{T}} |c_P|^2 \\right)^{1/2}\n", + "$$\n", + "While the bound is not rigorous, it will only be violated in pathological cases." + ] + }, + { + "cell_type": "markdown", + "id": "f302d808-b341-49cb-8bbd-b03f2bfc6dd6", + "metadata": {}, + "source": [ + "We again backpropagate six slices of the example circuit, using a maximum error per slice of `0.001` (this time, interpreted on the L2 norm)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c7b7e21f-22e4-479d-954c-39400974f8c1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=2)\n" + ] + } + ], + "source": [ + "l2_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=2)\n", + "print(l2_truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e92680c5-497e-47e0-ae0f-69465028dd66", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 6 circuit slices.\n", + "New observable contains 84 terms and 6 commuting groups.\n" + ] + } + ], + "source": [ + "max_slices = 6\n", + "l2_bp_obs, l2_remaining_slices, l2_metadata = backpropagate(\n", + " obs,\n", + " slices[-max_slices:],\n", + " truncation_error_budget=l2_truncation_error_budget,\n", + ")\n", + "l2_reduced_circuit = combine_slices(\n", + " slices[:-max_slices] + l2_remaining_slices\n", + ")\n", + "print(\n", + " f\"Backpropagated {max_slices - len(l2_remaining_slices)} circuit slices.\"\n", + ")\n", + "print(\n", + " f\"New observable contains {len(l2_bp_obs)} terms and {len(l2_bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a05f992a-3a38-4912-9e1b-bcd40463c35e", + "metadata": {}, + "source": [ + "Again, we compute the expectation value of the backpropagated observable and the error with respect to the exact reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "73fe737a-c3b0-4639-83dc-d614521dd77a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.317829770853422 0.00036808900872387085\n" + ] + } + ], + "source": [ + "estimator = StatevectorEstimator()\n", + "job = estimator.run([(l2_reduced_circuit, l2_bp_obs)])\n", + "res = job.result()\n", + "l2_exp = res[0].data.evs\n", + "l2_error = exact_exp - l2_exp\n", + "print(l2_exp, l2_error)" + ] + }, + { + "cell_type": "markdown", + "id": "81fd099c-f144-4c59-9b76-72fd8b06d8ad", + "metadata": {}, + "source": [ + "Plotting the incurred errors per slice and the accumulated error yields a similar picture as before." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fe7f454c-7d79-4c61-b1dd-6a98564fb5f1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n", + "axes[1].plot([6], [l2_error], \"x\", color=\"red\", label=\"actual error\")\n", + "plot_slice_errors(l2_metadata, axes[0])\n", + "plot_accumulated_error(l2_metadata, axes[1])" + ] + }, + { + "cell_type": "markdown", + "id": "9ef23a93-5c3b-48be-a02a-e666179c8c65", + "metadata": {}, + "source": [ + "Note that the accumulated error is again the sum of the single slice errors. This is another loose bound due to the Minkowski inequality, since we have to compute this bound recursively:\n", + "$$\n", + "|\\langle\\psi|\\Delta_{i}|\\psi\\rangle| \\leq |\\langle\\psi|\\tilde{\\Delta}_{i-1}|\\psi\\rangle| + \\left( \\sum_{P\\in\\mathcal{T_i}} |c_P|^2 \\right)^{1/2} = |\\langle\\psi|\\tilde{\\Delta}_{i}|\\psi\\rangle|\n", + "$$\n", + "where the new subscript $i$ indicates the current slice iteration, making $\\Delta_i$ the actual error at backpropagation iteration $i$, $\\tilde{\\Delta}_{i-1}$ the approximated truncation error from iteration $i-1$, and $\\mathcal{T}_i$ the set of Pauli terms truncated at iteration $i$." + ] + } + ], + "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": 5 +} diff --git a/docs/addons/qiskit-addon-obp/guides/index.mdx b/docs/addons/qiskit-addon-obp/guides/index.mdx new file mode 100644 index 00000000000..d10d7b702c1 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/index.mdx @@ -0,0 +1,13 @@ +--- +title: "OBP Guides" +description: "OBP Guides for the latest version of Operator backpropagation (OBP)" +--- + +# OBP Guides + +* [Overview](overview) +* [Quick start guide](quickstart) +* [Bound error in experiments](bound-error-using-p-norm) +* [Simulate circuits with OBP](simulating-circuits-with-obp) +* [Truncate operator terms during backpropagation](truncate-operator-terms) + diff --git a/docs/addons/qiskit-addon-obp/guides/overview.mdx b/docs/addons/qiskit-addon-obp/guides/overview.mdx new file mode 100644 index 00000000000..3d53964e88b --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/overview.mdx @@ -0,0 +1,23 @@ +--- +title: "Overview" +description: "Overview for the latest version of Operator backpropagation (OBP)" +--- + +# Overview + +This page summarizes the guides that are available. All guides are written to focus on specific aspects of the package. Examples of end-to-end workflows can be found in the tutorials hosted on the IBM Quantum Platform. + +## Getting started + +These simple guides help you to get started quickly with the package. + +* [Quick start](quickstart) + +## Beyond the basics + +These guides provide a deeper explanation of specific concepts and components from this package. + +* [Bound error in experiments](bound-error-using-p-norm) +* [Truncate operator terms during backpropagation](truncate-operator-terms) +* [Simulate circuits with OBP](simulating-circuits-with-obp) + diff --git a/docs/addons/qiskit-addon-obp/guides/quickstart.ipynb b/docs/addons/qiskit-addon-obp/guides/quickstart.ipynb new file mode 100644 index 00000000000..aed1268c292 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/quickstart.ipynb @@ -0,0 +1,44 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Coming soon\"\n", + "description: \"Coming soon for the latest version of Operator backpropagation (OBP)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "930fb583-8eca-4613-b4df-8ecf982d4a07", + "metadata": {}, + "source": [ + "# Coming soon" + ] + } + ], + "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": 5 +} diff --git a/docs/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp.ipynb b/docs/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp.ipynb new file mode 100644 index 00000000000..1af27ad2729 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp.ipynb @@ -0,0 +1,585 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Classically simulating circuits with OBP\"\n", + "description: \"Classically simulating circuits with OBP for the latest version of Operator backpropagation (OBP)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "26d926db-2e2f-41ff-947e-9757a9f426d8", + "metadata": {}, + "source": [ + "# Classically simulating circuits with OBP" + ] + }, + { + "cell_type": "markdown", + "id": "7642d6db-fe6d-4506-8e49-6c840f166e2b", + "metadata": {}, + "source": [ + "In this guide, you will learn how to classically simulate `QuantumCircuit` instances to estimate expectation values entirely through the means of OBP.\n", + "\n", + "Since OBP will take an observable and backpropagate it through a given circuit, the \"simulation\" of a circuit amounts to computing the expectation value of the target observable with respect to this circuit.\n", + "As you will see later, the `qiskit-addon-obp` package is even capable of handling simple noise models, allowing you to compute noisy expectation values, too!" + ] + }, + { + "cell_type": "markdown", + "id": "2e996d79-36fd-4a71-bd6b-737fc8e44987", + "metadata": {}, + "source": [ + "## Constructing an example circuit\n", + "\n", + "For the purposes of this guide, we will use the same example circuit as in the [Pauli term truncation guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html):" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b7946767-904b-422e-b076-7f956f3fdb70", + "metadata": {}, + "outputs": [], + "source": [ + "import rustworkx.generators\n", + "from qiskit.synthesis import LieTrotter\n", + "from qiskit_addon_utils.problem_generators import (\n", + " PauliOrderStrategy,\n", + " generate_time_evolution_circuit,\n", + " generate_xyz_hamiltonian,\n", + ")\n", + "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n", + "\n", + "# we generate a linear chain of 10 qubits\n", + "num_qubits = 10\n", + "linear_chain = rustworkx.generators.path_graph(num_qubits)\n", + "\n", + "# we use an arbitrary XY model\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " linear_chain,\n", + " coupling_constants=(0.05, 0.02, 0.0),\n", + " ext_magnetic_field=(0.02, 0.08, 0.0),\n", + " pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,\n", + ")\n", + "# we evolve for some time\n", + "circuit = generate_time_evolution_circuit(\n", + " hamiltonian, synthesis=LieTrotter(reps=3), time=2.0\n", + ")\n", + "# slice the circuit by gate type\n", + "slices = slice_by_gate_types(circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "63bcf4d0-7abe-4467-bc2b-f95d8d66dee5", + "metadata": {}, + "source": [ + "However, the above is purely the circuit describing the time evolution under a chosen Hamiltonian.\n", + "We also need an initial state to start from, with respect to which we compute the expectation values of our observable.\n", + "\n", + "Of course, we could choose the all-zero (or vacuum) state as our initial state, but to show how one would insert their own initial state, we choose a different one below.\n", + "\n", + "One possibility, would be to prepend the initial state to our time-evolution circuit above: `circuit.compose(initial_state, front=True)`.\n", + "But since we have already sliced our `circuit`, it is easier to simply insert the initial state as the first slice, which we do below.\n", + "\n", + "In this way, we can simply replace the first slice with another initial state, if we want to exchange that in the future, without having to recompute our slices." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ffaa07e2-af75-4424-9e7b-61c81b3bb9ff", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.circuit import QuantumCircuit\n", + "\n", + "initial_state = QuantumCircuit(num_qubits)\n", + "for i in range(0, num_qubits, 2):\n", + " initial_state.x(i)\n", + "\n", + "slices.insert(0, initial_state)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8758f393-8691-4116-94ca-1185c6188bb4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# for visualization purposes only, we recombine the slices with barriers between them and draw the resulting circuit\n", + "combine_slices(slices, include_barriers=True).draw(\"mpl\", fold=50, scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "bc9d51da-0a4e-46e2-8a5a-3501456aeb81", + "metadata": {}, + "source": [ + "## Simulating a noiseless expectation value" + ] + }, + { + "cell_type": "markdown", + "id": "1125cb39-4eda-4928-86b2-24e1872ed87e", + "metadata": {}, + "source": [ + "As our target observable, we choose the `ZZ` observable on the central qubits:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ee438fc3-8196-43d9-bcdb-956fd9ec4cf4", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "obs = SparsePauliOp(\"IIIIZZIIII\")" + ] + }, + { + "cell_type": "markdown", + "id": "d06f10b0-683e-476e-9ac3-5c110364a0c8", + "metadata": {}, + "source": [ + "At this point, we are already set to classically simulate the expectation value using OBP.\n", + "To do so, we simply provide the _all_ the slices to the `backpropagate` method, like so:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6402f5f5-08e0-4d05-ab2b-0c8863e9f966", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_obp import backpropagate\n", + "\n", + "vacuum_state_obs, _, metadata = backpropagate(obs, slices)" + ] + }, + { + "cell_type": "markdown", + "id": "7b2d555e-7fab-49f1-82d4-74a770f1be75", + "metadata": {}, + "source": [ + "We have now backpropagated our target observable `obs` through the _entire_ circuit (**including** the `initial_state` which we placed on `slices[0]`) resulting in a new `SparsePauliOp` whose expectation value we obtain by projecting it on the _vacuum state_ (`|00...00>`).\n", + "\n", + "This can be achieved in a straight forward manner by summing up the coefficients of all Pauli terms defined in the computational basis:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "63ae0edc-e8f6-467b-896a-ddd451d40a9a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.complex128(-0.8285688012239535+4.9487770271457865e-20j)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vacuum_state_obs.coeffs[~vacuum_state_obs.paulis.x.any(axis=1)].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "107a0d89-323c-469b-8442-5e3f48169cd4", + "metadata": {}, + "source": [ + "As a sanity check (and to prove that this works) we can compare our result against Qiskit's `Statevector`:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f3cb999d-688a-4e82-bf12-d958ef0e2ec0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.complex128(-0.8285687255430366+0j)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.quantum_info import Statevector\n", + "\n", + "Statevector(combine_slices(slices)).expectation_value(obs)" + ] + }, + { + "cell_type": "markdown", + "id": "02207cde-e89e-4cf6-8b3e-727a750d245c", + "metadata": {}, + "source": [ + "### Some notes on performance\n", + "\n", + "The computational efficiency of the `backpropagate` call above will heavily depend on many things, including:\n", + "- the structure of the `circuit`\n", + "- the method of slicing the circuit\n", + "- the target observable\n", + "- the truncation parameters\n", + "\n", + "Since the `backpropagate` method simplifies the observable after every _slice_ has been applied, the number of gates in a slice can dramatically influence the computational burden.\n", + "The most aggressive strategy in terms of operator simplification can be achieved by slicing your circuit into slices of individual gates.\n", + "\n", + "Additionally, you can leverage all of the truncation mechanism built into the `backpropagate` method.\n", + "We did not do so above, effectively resulting in an exact expectation value, but you can learn how to in the [Pauli term truncation guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html)." + ] + }, + { + "cell_type": "markdown", + "id": "1ba37abf-0f78-4053-8995-2ab8340237ab", + "metadata": {}, + "source": [ + "## Simulating a noisy expectation value" + ] + }, + { + "cell_type": "markdown", + "id": "3a5e753b-c479-42d9-84d0-5ce4860ec091", + "metadata": {}, + "source": [ + "The `qiskit-addon-obp` package also supports handling of noise models in the form of `PauliLindbladError`s.\n", + "This is especially useful when you have characterized the noise model of the 2-qubit layers in your circuit, for example using the [`NoiseLearner`](/api/qiskit-ibm-runtime/noise-learner-noise-learner).\n", + "\n", + "In this section, you will see how you can use the `LayerError` objects returned by the `NoiseLearner` to compute noisy expectation values using OBP." + ] + }, + { + "cell_type": "markdown", + "id": "2d50dbf1-3abd-4945-bed9-1ed49758e9cc", + "metadata": {}, + "source": [ + "### Obtaining a noise model\n", + "\n", + "Normally, you would execute the `NoiseLearner` to obtain a noise model of your specific circuit.\n", + "To avoid complexity (and randomness) in this tutorial, we will refrain from doing so, and instead hard-code some noise model for our circuit below.\n", + "\n", + "However, we make sure that the structure of our data matches that of the [`NoiseLearnerResult`](/api/qiskit-ibm-runtime/noise-learner-result)." + ] + }, + { + "cell_type": "markdown", + "id": "54a7aba1-a7ee-4d0f-b95b-095158511ce0", + "metadata": {}, + "source": [ + "In its current (non-transpiled) form, our circuit contains 4 unique layers of 2-qubit gates:\n", + "- `slices[1]`: which has `Rxx` gates acting on all odd pairs of qubits\n", + "- `slices[2]`: which has `Rxx` gates acting on all even pairs of qubits\n", + "- `slices[3]`: which has `Ryy` gates acting on all odd pairs of qubits\n", + "- `slices[4]`: which has `Ryy` gates acting on all even pairs of qubits\n", + "\n", + "In the cell below, we manually construct 4 `LayerError` instances for each one of these layers with some randomized error rates." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e7a3b1f9-b60c-4735-8d6b-5151a7c5b92b", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.quantum_info import PauliList\n", + "from qiskit_ibm_runtime.utils.noise_learner_result import (\n", + " LayerError,\n", + " PauliLindbladError,\n", + ")\n", + "\n", + "# fmt: off\n", + "pauli_errors_even = ['IIIIIIIIIX', 'IIIIIIIIIY', 'IIIIIIIIIZ', 'IIIIIIIIXI', 'IIIIIIIIXX', 'IIIIIIIIXY', 'IIIIIIIIXZ', 'IIIIIIIIYI', 'IIIIIIIIYX', 'IIIIIIIIYY', 'IIIIIIIIYZ', 'IIIIIIIIZI', 'IIIIIIIIZX', 'IIIIIIIIZY', 'IIIIIIIIZZ', 'IIIIIIIXII', 'IIIIIIIXXI', 'IIIIIIIXYI', 'IIIIIIIXZI', 'IIIIIIIYII', 'IIIIIIIYXI', 'IIIIIIIYYI', 'IIIIIIIYZI', 'IIIIIIIZII', 'IIIIIIIZXI', 'IIIIIIIZYI', 'IIIIIIIZZI', 'IIIIIIXIII', 'IIIIIIXXII', 'IIIIIIXYII', 'IIIIIIXZII', 'IIIIIIYIII', 'IIIIIIYXII', 'IIIIIIYYII', 'IIIIIIYZII', 'IIIIIIZIII', 'IIIIIIZXII', 'IIIIIIZYII', 'IIIIIIZZII', 'IIIIIXIIII', 'IIIIIXXIII', 'IIIIIXYIII', 'IIIIIXZIII', 'IIIIIYIIII', 'IIIIIYXIII', 'IIIIIYYIII', 'IIIIIYZIII', 'IIIIIZIIII', 'IIIIIZXIII', 'IIIIIZYIII', 'IIIIIZZIII', 'IIIIXIIIII', 'IIIIXXIIII', 'IIIIXYIIII', 'IIIIXZIIII', 'IIIIYIIIII', 'IIIIYXIIII', 'IIIIYYIIII', 'IIIIYZIIII', 'IIIIZIIIII', 'IIIIZXIIII', 'IIIIZYIIII', 'IIIIZZIIII', 'IIIXIIIIII', 'IIIXXIIIII', 'IIIXYIIIII', 'IIIXZIIIII', 'IIIYIIIIII', 'IIIYXIIIII', 'IIIYYIIIII', 'IIIYZIIIII', 'IIIZIIIIII', 'IIIZXIIIII', 'IIIZYIIIII', 'IIIZZIIIII', 'IIXIIIIIII', 'IIXXIIIIII', 'IIXYIIIIII', 'IIXZIIIIII', 'IIYIIIIIII', 'IIYXIIIIII', 'IIYYIIIIII', 'IIYZIIIIII', 'IIZIIIIIII', 'IIZXIIIIII', 'IIZYIIIIII', 'IIZZIIIIII', 'IXIIIIIIII', 'IXXIIIIIII', 'IXYIIIIIII', 'IXZIIIIIII', 'IYIIIIIIII', 'IYXIIIIIII', 'IYYIIIIIII', 'IYZIIIIIII', 'IZIIIIIIII', 'IZXIIIIIII', 'IZYIIIIIII', 'IZZIIIIIII', 'XIIIIIIIII', 'XXIIIIIIII', 'XYIIIIIIII', 'XZIIIIIIII', 'YIIIIIIIII', 'YXIIIIIIII', 'YYIIIIIIII', 'YZIIIIIIII', 'ZIIIIIIIII', 'ZXIIIIIIII', 'ZYIIIIIIII', 'ZZIIIIIIII']\n", + "pauli_errors_odd = ['IIIIIIIIXI', 'IIIIIIIIYI', 'IIIIIIIIZI', 'IIIIIIIXII', 'IIIIIIIXXI', 'IIIIIIIXYI', 'IIIIIIIXZI', 'IIIIIIIYII', 'IIIIIIIYXI', 'IIIIIIIYYI', 'IIIIIIIYZI', 'IIIIIIIZII', 'IIIIIIIZXI', 'IIIIIIIZYI', 'IIIIIIIZZI', 'IIIIIIXIII', 'IIIIIIXXII', 'IIIIIIXYII', 'IIIIIIXZII', 'IIIIIIYIII', 'IIIIIIYXII', 'IIIIIIYYII', 'IIIIIIYZII', 'IIIIIIZIII', 'IIIIIIZXII', 'IIIIIIZYII', 'IIIIIIZZII', 'IIIIIXIIII', 'IIIIIXXIII', 'IIIIIXYIII', 'IIIIIXZIII', 'IIIIIYIIII', 'IIIIIYXIII', 'IIIIIYYIII', 'IIIIIYZIII', 'IIIIIZIIII', 'IIIIIZXIII', 'IIIIIZYIII', 'IIIIIZZIII', 'IIIIXIIIII', 'IIIIXXIIII', 'IIIIXYIIII', 'IIIIXZIIII', 'IIIIYIIIII', 'IIIIYXIIII', 'IIIIYYIIII', 'IIIIYZIIII', 'IIIIZIIIII', 'IIIIZXIIII', 'IIIIZYIIII', 'IIIIZZIIII', 'IIIXIIIIII', 'IIIXXIIIII', 'IIIXYIIIII', 'IIIXZIIIII', 'IIIYIIIIII', 'IIIYXIIIII', 'IIIYYIIIII', 'IIIYZIIIII', 'IIIZIIIIII', 'IIIZXIIIII', 'IIIZYIIIII', 'IIIZZIIIII', 'IIXIIIIIII', 'IIXXIIIIII', 'IIXYIIIIII', 'IIXZIIIIII', 'IIYIIIIIII', 'IIYXIIIIII', 'IIYYIIIIII', 'IIYZIIIIII', 'IIZIIIIIII', 'IIZXIIIIII', 'IIZYIIIIII', 'IIZZIIIIII', 'IXIIIIIIII', 'IXXIIIIIII', 'IXYIIIIIII', 'IXZIIIIIII', 'IYIIIIIIII', 'IYXIIIIIII', 'IYYIIIIIII', 'IYZIIIIIII', 'IZIIIIIIII', 'IZXIIIIIII', 'IZYIIIIIII', 'IZZIIIIIII']\n", + "# fmt: on\n", + "\n", + "np.random.seed(42)\n", + "\n", + "layer_error_odd_xx = LayerError(\n", + " circuit=slices[1],\n", + " qubits=list(range(num_qubits)),\n", + " error=PauliLindbladError(\n", + " PauliList(pauli_errors_odd),\n", + " 0.0001 + 0.0004 * np.random.rand(len(pauli_errors_odd)),\n", + " ),\n", + ")\n", + "\n", + "layer_error_even_xx = LayerError(\n", + " circuit=slices[2],\n", + " qubits=list(range(num_qubits)),\n", + " error=PauliLindbladError(\n", + " PauliList(pauli_errors_even),\n", + " 0.0001 + 0.0004 * np.random.rand(len(pauli_errors_even)),\n", + " ),\n", + ")\n", + "\n", + "layer_error_odd_yy = LayerError(\n", + " circuit=slices[3],\n", + " qubits=list(range(num_qubits)),\n", + " error=PauliLindbladError(\n", + " PauliList(pauli_errors_odd),\n", + " 0.0001 + 0.0004 * np.random.rand(len(pauli_errors_odd)),\n", + " ),\n", + ")\n", + "\n", + "layer_error_even_yy = LayerError(\n", + " circuit=slices[4],\n", + " qubits=list(range(num_qubits)),\n", + " error=PauliLindbladError(\n", + " PauliList(pauli_errors_even),\n", + " 0.0001 + 0.0004 * np.random.rand(len(pauli_errors_even)),\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a9dff7ad-2c7a-4fc2-b7b5-5aa1a0e9b9db", + "metadata": {}, + "source": [ + "If you would have used the `NoiseLearner` to identify the unique 2-qubit gate layers of your circuit and characterize their noise, you would obtain a `NoiseLearnerResult` object.\n", + "This result would contain a list of `LayerError` objects, just like the ones we have manually constructed above.\n", + "\n", + "For each unique 2-qubit layer, the `LayerError` contains:\n", + "- the `QuantumCircuit` representing that 2-qubit gate layer\n", + "- the qubit indices which this circuit is acting upon\n", + "- the `PauliLindbladError` which represents the characterized noise model of this layer\n", + "\n", + "The `PauliLindbladError` will contain two objects:\n", + "- the list of Pauli errors that have been characterized\n", + "- the error rates corresponding to each one of those Pauli errors\n", + "\n", + "Normally, the list of Pauli errors will be sparse. More specifically, it will contain the single-qubit Pauli errors on all qubits that have gates acting upon them as well as the two-qubit Pauli errors on all those qubits that are connected." + ] + }, + { + "cell_type": "markdown", + "id": "9c3a1a9d-6fcb-4345-b9ba-e86e2e362a7e", + "metadata": {}, + "source": [ + "### Inserting the noisy layers into our circuit\n", + "\n", + "In the previous section, we have specifically constructed one `LayerError` for each of our known unique 2-qubit gate layers.\n", + "This means, we know which `LayerError` matches a specific one of our slices exactly.\n", + "\n", + "Normally, when using the `LayerError`, you will need to figure out what the unique 2-qubit gate layer is, and where it occurs inside of your circuit.\n", + "You will then need to adjust your `circuit` and/or `slices` to insert the `LayerError` accordingly.\n", + "How to do this in the general case, is beyond the scope of this how-to guide.\n", + "**TODO: link to external documentation, once it exists!**" + ] + }, + { + "cell_type": "markdown", + "id": "dcb5937e-83d8-41ab-a526-d6677201c8f4", + "metadata": {}, + "source": [ + "Here, our life is simpler because we know which slice a `LayerError` corresponds to.\n", + "Therefore, it is now just a matter of inserting new slices to represent the noise.\n", + "\n", + "Note, that we must wrap each `PauliLindbladError` from `LayerError.error` in a `PauliLindbladErrorInstruction` for it to be a valid `QuantumCircuit` instruction." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1f5c4655-f6af-439f-8475-0ecf0c673de5", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_obp.utils.noise import PauliLindbladErrorInstruction\n", + "\n", + "noisy_slices = []\n", + "for slice_ in slices:\n", + " if slice_ == layer_error_even_xx.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [\n", + " (\n", + " PauliLindbladErrorInstruction(\n", + " layer_error_even_xx.error\n", + " ),\n", + " slice_.qubits,\n", + " )\n", + " ]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_odd_xx.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [\n", + " (\n", + " PauliLindbladErrorInstruction(\n", + " layer_error_odd_xx.error\n", + " ),\n", + " slice_.qubits,\n", + " )\n", + " ]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_even_yy.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [\n", + " (\n", + " PauliLindbladErrorInstruction(\n", + " layer_error_even_yy.error\n", + " ),\n", + " slice_.qubits,\n", + " )\n", + " ]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_odd_yy.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [\n", + " (\n", + " PauliLindbladErrorInstruction(\n", + " layer_error_odd_yy.error\n", + " ),\n", + " slice_.qubits,\n", + " )\n", + " ]\n", + " )\n", + " )\n", + " noisy_slices.append(slice_)" + ] + }, + { + "cell_type": "markdown", + "id": "b3778d94-4d04-4094-990d-a354f2062932", + "metadata": {}, + "source": [ + "We can check our work and draw the circuit below:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "dadc314d-452f-465c-a382-d54685ce3f2a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "combine_slices(noisy_slices, include_barriers=True).draw(\n", + " \"mpl\", fold=100, scale=0.8\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "10b88555-0263-4cdb-a4c1-6895d7e55a5b", + "metadata": {}, + "source": [ + "### Simulating a noisy expectation value\n", + "\n", + "At this point, classically simulating the expectation value works exactly the same as before, just" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0e3ecb85-08c4-4fda-85e2-1000983c8842", + "metadata": {}, + "outputs": [], + "source": [ + "vacuum_state_noisy_obs, _, metadata = backpropagate(obs, noisy_slices)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a74e9e88-2f1c-4d40-873d-bf4524e2b7fe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.complex128(-0.7230801696448901+7.082755280463563e-19j)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vacuum_state_noisy_obs.coeffs[\n", + " ~vacuum_state_noisy_obs.paulis.x.any(axis=1)\n", + "].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "7a4d2ff5-7c49-463e-8515-6fc9daa951ad", + "metadata": {}, + "source": [ + "We point out again, that multiple performance concerns should be considered.\n", + "Please go back to the [corresponding section above](#some-notes-on-performance)." + ] + } + ], + "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": 5 +} diff --git a/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms.ipynb b/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms.ipynb new file mode 100644 index 00000000000..4ddb0ee7bbc --- /dev/null +++ b/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms.ipynb @@ -0,0 +1,947 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Truncate Pauli terms during backpropagation\"\n", + "description: \"Truncate Pauli terms during backpropagation for the latest version of Operator backpropagation (OBP)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "11cf5076-2777-499c-be50-531e305bf011", + "metadata": {}, + "source": [ + "# Truncate Pauli terms during backpropagation" + ] + }, + { + "cell_type": "markdown", + "id": "c1453472-3506-4008-b432-f8ef9ccbf3ee", + "metadata": {}, + "source": [ + "This guide explains how to configure the Pauli term truncation mechanism provided by the [qiskit_addon_obp.utils.truncating](/docs/api/qiskit-addon-obp/utils-truncating#module-qiskit_addon_obp.utils.truncating) module.\n", + "\n", + "Operator backpropagation (OBP) can be used to reduce the depth of quantum circuits at the cost of a more complex observable. In order to get meaningful results from OBP, one usually needs to truncate terms from their observable to prevent it from growing too large. One way to allow for deeper backpropagation into the circuit, while preventing the operator from growing too large, is to truncate terms with small coefficients, rather than adding them to the operator. Truncating terms can result in fewer quantum circuits to execute, but doing so results in some error in the final expectation value calculation, proportional to the magnitude of the truncated terms' coefficients.\n", + "\n", + "The [backpropagate](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) method takes an optional [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget), which configures the truncation of low-weight Pauli terms for each observable after the successful backpropagation of every slice.\n", + "The amount of terms that are truncated depends on various configuration parameters specified by the user.\n", + "As of now, only a single truncation strategy is available - the [truncate_binary_search](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search) method.\n", + "Given an observable and some _budget_ it will perform a binary search over the Pauli terms and coefficients within that observable to find the optimal threshold,\n", + "such that the sum of the truncated coefficients is maximal but lower than the budget.\n", + "\n", + "**Note**: By default, the L1 norm is used to evaluate and bound the truncation error; however, the `p_norm` setting enables the specification of the Lp-norm to be used.\n", + "For more information on how to use that setting, refer to the [Use different Lp-norms for Pauli term truncation](https://qiskit.github.io/qiskit-addon-obp/how_tos/bound_error_using_p_norm.html) guide." + ] + }, + { + "cell_type": "markdown", + "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30", + "metadata": {}, + "source": [ + "The following examples illustrate various ways of building an [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) by using the accompanying [setup_budget](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget) function." + ] + }, + { + "cell_type": "markdown", + "id": "c83c0741-4a9e-4f89-92a9-4756ef133130", + "metadata": {}, + "source": [ + "## Construct an example circuit\n", + "\n", + "This guide uses the following circuit slices:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "297fc3b0-d3b4-432d-96cd-7e175b7e6a52", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import rustworkx.generators\n", + "from qiskit.synthesis import LieTrotter\n", + "from qiskit_addon_utils.problem_generators import (\n", + " PauliOrderStrategy,\n", + " generate_time_evolution_circuit,\n", + " generate_xyz_hamiltonian,\n", + ")\n", + "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n", + "\n", + "# Generate a linear chain of 10 qubits\n", + "linear_chain = rustworkx.generators.path_graph(10)\n", + "\n", + "# Use an arbitrary XY model\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " linear_chain,\n", + " coupling_constants=(0.05, 0.02, 0.0),\n", + " ext_magnetic_field=(0.02, 0.08, 0.0),\n", + " pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,\n", + ")\n", + "# Evolve for some time\n", + "circuit = generate_time_evolution_circuit(\n", + " hamiltonian, synthesis=LieTrotter(reps=3), time=2.0\n", + ")\n", + "# slice the circuit by gate type\n", + "slices = slice_by_gate_types(circuit)\n", + "\n", + "# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit\n", + "combine_slices(slices, include_barriers=True).draw(\"mpl\", fold=50, scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "89ce4fa9-06ba-4009-8594-d1b85f5e719f", + "metadata": {}, + "source": [ + "We will look at a single simple observable:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b168d34c-f147-4524-9af9-264f56440a55", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "obs = SparsePauliOp(\"IIIIIZIIII\")" + ] + }, + { + "cell_type": "markdown", + "id": "723dcf4e-1dab-4c26-a8e4-cd30b1564189", + "metadata": {}, + "source": [ + "## The simplest case: a fixed truncation budget for each slice" + ] + }, + { + "cell_type": "markdown", + "id": "5e04e5c4-1e6b-4720-9563-becbb9edc4d7", + "metadata": {}, + "source": [ + "The available budget for truncation of Pauli terms can vary at each step of the backpropagation.\n", + "To understand how this works, we first look at the simplest case of a fixed truncation budget as specified by the user.\n", + "\n", + "The most straightforward way to specify the truncation budget is by using the `max_error_per_slice` argument.\n", + "In fact, this is what is being done in the [Reducing depth of circuits with operator backpropagation](https://qiskit.github.io/qiskit-addon-obp/tutorials/01_getting_started.ipynb) tutorial. Setting `max_error_per_slice` to a `float` results in each slice being allotted a budget equaling that value.\n", + "In the example below, we set this value to `0.001` which guarantees an implicit truncation error of at most `0.018`, if all 18 slices were backpropagated.\n", + "\n", + "
\n", + "Note that any error budget remaining after backpropagating a slice and truncating terms with small coefficients will always be added to the following slice's error budget.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5f20a9b9-c3c4-43c4-9f13-fdf451958081", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n" + ] + } + ], + "source": [ + "from qiskit_addon_obp.utils.truncating import setup_budget\n", + "\n", + "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c3b8b9ef-7d5c-43dd-8cb9-90756d4753b7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 11 circuit slices.\n", + "New observable contains 29 terms and 10 commuting groups.\n" + ] + } + ], + "source": [ + "from qiskit_addon_obp import backpropagate\n", + "from qiskit_addon_obp.utils.simplify import OperatorBudget\n", + "\n", + "op_budget = OperatorBudget(max_qwc_groups=10)\n", + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4a772af6-edc6-4e53-81c7-53f3b72bf58a", + "metadata": {}, + "source": [ + "We next use the [OBPMetadata](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata) instance and the tools provided by the [visualization](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization) module to visualize the backpropagation process.\n", + "\n", + "- **The top left plot** shows that we have enough budget to begin truncating observable terms after backpropagating the third slice. Starting from the third slice, we know that we truncate at least one term from each slice we backpropagate because we are incurring some truncation error after each slice.\n", + "- **The top right plot** shows that the error budget grows to `.003` for the third slice. We see a sharp drop in remaining budget, which means terms were truncated from the observable. This is consistent with what we inferred from the top left plot.\n", + "- **The bottom left plot** shows that as we truncate terms from our observable, our overall accumulated error is growing monotonically. This graph also confirms that no terms were truncated until after the third slice had been backpropagated.\n", + "- **The bottom right plot** shows that the number of commuting Pauli groups in our observable has grown to the specified limit of `10`. This plot also shows how backpropagating one more layer would cause our observable to outgrow the specified bound as you can see from the crossover of the black and red lines." + ] + }, + { + "cell_type": "markdown", + "id": "8ff436fa-3b52-4387-a9f4-d8d9df13c480", + "metadata": {}, + "source": [ + "
\n", + "Note that in all of these plots, the x-axis enumerates the backpropagated slices, but since OBP works on the end of the circuit, `slice 1` is the very last slice, `slice 2` is the one before that, and so on.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "68f118df-6718-49b2-ba80-c37ef461f71a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "from qiskit_addon_obp.utils.visualization import (\n", + " plot_accumulated_error,\n", + " plot_left_over_error_budget,\n", + " plot_num_qwc_groups,\n", + " plot_slice_errors,\n", + ")\n", + "\n", + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "523f5a81-afbc-4eee-9d66-05cc3b4a781a", + "metadata": {}, + "source": [ + "## Specify slice budget explicitly" + ] + }, + { + "cell_type": "markdown", + "id": "80f9337d-ea37-454c-ae0b-b101259568c9", + "metadata": {}, + "source": [ + "If you know how to assign a budget to each slice, such that the backpropagation performance is optimized, you might want to explicitly assign a budget for each slice. For demonstration purposes, we will assign the first three slices zero budget and use a budget of `.001` per slice for the remaining slices." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "17ce7232-7e3a-44e4-8081-1b1b27430bad", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)\n" + ] + } + ], + "source": [ + "# Zero out the first 3 slices' budgets\n", + "max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)\n", + "\n", + "truncation_error_budget = setup_budget(\n", + " max_error_per_slice=max_error_per_slice\n", + ")\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7cd711a7-1aea-4b45-a46a-63d771230969", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 11 circuit slices.\n", + "New observable contains 32 terms and 10 commuting groups.\n" + ] + } + ], + "source": [ + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3e853723-454b-4c69-b9c5-180eb1b2e6c2", + "metadata": {}, + "source": [ + "Removing the budget from the first three layers resulted in no terms being truncated until after the fourth layer, as can be confirmed in three of these plots. What might be somewhat surprising is that although the fourth slice had no leftover budget passed to it, a term was still truncated by using the allocated `.001` budget. This is obvious from the **top left** plot, but can also be observed in the **top right** plot, as the leftover budget curve flattens between slices three and four. Another notable detail is that at least one term was truncated from this point on, the same as in the previous example.\n", + "\n", + "The key takeaway is that although we incurred less truncation error in the second example due to zeroing out some slices' budgets, we were able to backpropagate the same number of slices, and our observable contains the same number of commuting Pauli groups. We can confirm that the bound on our error is smaller in the second example by inspecting the **bottom left** plot." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b7167f80-5d29-4c32-be97-cba733f18b1d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "f03a92ee-e9a0-4707-b23a-ebdc3c9f35a3", + "metadata": {}, + "source": [ + "## Specify the budget cyclically" + ] + }, + { + "cell_type": "markdown", + "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9", + "metadata": {}, + "source": [ + "If you have a circuit with some repeatable pattern, such as a Trotter circuit, you might want to specify a budget for that repeated subset of slices and have that budget used for all of the following repetitions of those slices.\n", + "\n", + "More concretely, the example circuit we are using has six slices that are repeated three times, for a total of 18 slices. We will arbitrarily assign zero budget to the single-qubit and `RYY` layers, and `.003` budget to each of the `RXX` layers. We will observe how specifying the budget as a length-6 sequence causes the budget to be applied to all 18 slices cyclically.\n", + "\n", + "
\n", + "Again, note that the slices are backpropagated in reverse order (that is, starting at the end). Therefore, the first entry in our cyclic budget actually gets used for the last slice, the second entry for the slice before that, and so on.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f37a576-5a61-4589-a896-8304056d0e19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)\n" + ] + } + ], + "source": [ + "# Specify a length-6 per-slice budget.\n", + "# This will be cycled over three times to be applied to the 18 slices\n", + "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n", + "\n", + "truncation_error_budget = setup_budget(\n", + " max_error_per_slice=max_error_per_slice\n", + ")\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "7f78cfe4-13e4-472c-b6cc-9d1e867cbd9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 13 circuit slices.\n", + "New observable contains 49 terms and 14 commuting groups.\n" + ] + } + ], + "source": [ + "op_budget = OperatorBudget(max_qwc_groups=20)\n", + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3511206b-2972-41ba-b981-6c20ccb7712b", + "metadata": {}, + "source": [ + "As seen in the **top left** and **bottom left** images, no truncation was performed over the first four slices since they were allocated no error budget. Slices five and six had some of their terms truncated as budget became available.\n", + "\n", + "As seen in the **top left** image, a relatively large amount of error was incurred after backpropagating slice seven, even though that slice was allocated no budget. This is because only about `.002` of the `.006` total budget allocated to slices five and six was used, so the remaining was forwarded to slice seven and mostly expended, as seen in the **top right** image.\n", + "\n", + "The **top left** and **top right** images show that the small amount of leftover budget is used up between slices 8-10, and new budget becomes available in slice 11, as expected. The **top left**, **top right**, and **bottom left** images all demonstrate the cyclical behavior of the `max_error_per_slice` argument when its length is less than the number of slices. This cyclical behavior would have continued through all the slices, but the `max_qwc_groups` stopping criteria was reached after backpropagating 13 slices, as seen in the **bottom right** image.\n", + "\n", + "Also interesting is that the number of Pauli groups actually drops after the second round of budget becomes available at slice 11. This is because there were groups with small coefficients that could not be truncated until there was more budget after backpropagating slice 11, so they accumulated in the observable for several iterations. This case also highlights that `max_qwc_groups` must be _exceeded_ for the algorithm to terminate." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4c36768e-03e7-4e1b-81b3-59b6e9eab43e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "269536c5-8ba4-48da-8e99-6b8ce3a52bd0", + "metadata": {}, + "source": [ + "## Cap the total error\n", + "\n", + "In addition to specifying the per-slice error budget, one can specify the maximum amount of error to incur from truncation. Once that limit is hit, no more truncation will be performed; however, backpropagation will continue until the observable becomes too large and one of the stopping criteria is met.\n", + "\n", + "We next re-run the above experiment, setting a cap on `max_error_total` such that the error budget is expended after backpropagating slice seven." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8ef6a100-fb4b-4b66-be6e-a094db2c29d4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)\n" + ] + } + ], + "source": [ + "# Specify a length-6 per-slice budget.\n", + "# This will be cycled over 3 times to be applied to the 18 slices\n", + "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n", + "\n", + "truncation_error_budget = setup_budget(\n", + " max_error_per_slice=max_error_per_slice, max_error_total=0.006\n", + ")\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "c58bccd5-aa40-482f-acaf-4ef50d420053", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 10 circuit slices.\n", + "New observable contains 67 terms and 20 commuting groups.\n" + ] + } + ], + "source": [ + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "dadb77a5-00d7-427d-921b-4a005f5ab36f", + "metadata": {}, + "source": [ + "As expected, our truncation error caps out at `.006` (**bottom left** chart). It is notable that in this run we were not able to backpropagate slice 11. This is because we did not have enough budget to truncate terms and the number of commuting Pauli groups grew past the limit of `20`, as seen in the **bottom right** image." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "69c9069a-9038-4319-8dc6-37a1be973ebf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "2049d223-1b91-4042-ba98-a86f7a59483c", + "metadata": {}, + "source": [ + "It may be desirable to simply cap the overall budget with `max_error_total` without specifying `max_error_per_slice`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fac59f4f-3528-49c1-9d4d-9be98d2c7d78", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)\n" + ] + } + ], + "source": [ + "truncation_error_budget = setup_budget(max_error_total=0.018)\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "markdown", + "id": "20c55e75-f619-4ac9-aecd-29491867ab82", + "metadata": {}, + "source": [ + "The output of the cell above might be slightly surprising because the `per_slice_budget` is set to the `max_error_total`. This indicates that the entire available budget will be consumed **greedily**.\n", + "You can think of it this way: the entire budget is available to each slice (because we loop over the `per_slice_budget`). But any budget that has already been consumed will be deducted from the budget that is available at that given point in the algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "5d65a057-eb5e-4308-b84d-82bfbea06989", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 9 circuit slices.\n", + "New observable contains 25 terms and 9 commuting groups.\n" + ] + } + ], + "source": [ + "op_budget = OperatorBudget(max_qwc_groups=10)\n", + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a77ceb1e-bc6d-462d-aebe-ec478d312233", + "metadata": {}, + "source": [ + "The **top right** image shows how the entire `.018` error budget is made available to the first slice. No truncation is performed until the third slice, so the leftover budget remains constant. The budget monotonically decreases, as the full budget is made available to each backpropagated slice until it is expended.\n", + "\n", + "It is notable that this experiment yielded two fewer backpropagated slices compared to the first experiment in this notebook, which is almost identical. This demonstrates that for some problems, distributing the budget evenly might be optimal. For other problems, allowing slices to greedily expend the full budget might yield better performance." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "51d36d7e-0bd5-4388-bbb8-6831bf1f02e6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "8859d36c-258f-441f-a7cf-3997b9f6e8ff", + "metadata": {}, + "source": [ + "## Cap the number of backpropagated slices and the total error together\n", + "\n", + "If you don't want to distribute their error budget all the way through the circuit but you don't want to greedily expend it either, you can specify the number of slices you intend to backpropagate (`num_slices`), along with a total error budget (`max_error_total`). This will distribute the error budget uniformly (accordingly to `p_norm`) across the input slices.\n", + "\n", + "Here, we will cap the number of slices we might backpropagate at `12`, and we will keep the same total error budget." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "474cf708-5447-4984-873c-5f62f81a402d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)\n" + ] + } + ], + "source": [ + "num_slices = 12\n", + "\n", + "truncation_error_budget = setup_budget(\n", + " max_error_total=0.018, num_slices=num_slices, p_norm=1\n", + ")\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "markdown", + "id": "1ac2fb58-db97-4c9e-9a96-fce97b52e7e2", + "metadata": {}, + "source": [ + "Now we will attempt to backpropagate the 12 slices for which we allocated some budget in the previous step. We do this by just passing in the final 12 slices in our circuit. With `p_norm=1`, each of the 12 slices has an available budget of `0.018 / num_slices = 0.0015`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "6efc92bc-3433-4ea6-a683-684205201f4f", + "metadata": {}, + "outputs": [], + "source": [ + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices[-num_slices:],\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "b15c2a20-19c3-417c-b32d-173c10f13852", + "metadata": {}, + "source": [ + "Since we passed a subset of our slices (in the code ``slices[-num_slices:]``) to ``backpropagate``, we must combine the slices remaining after backpropagation with the slices that were never sent for backpropagation (in the code ``slices[:-num_slices]``).\n", + "\n", + "Once we have combined all of the remaining slices, we can use [combine_slices](/docs/api/qiskit-addon-utils/slicing#combine_slices) to generate the reduced-depth [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit#html). Next, we inspect how many slices were backpropagated versus how large our observable became." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "7c42256f-e7f2-40ed-999e-6a51fd9351c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 12 circuit slices.\n", + "New observable contains 29 terms and 9 commuting groups.\n" + ] + } + ], + "source": [ + "# Recombine the slices remaining after backprop with the rest of the original circuit\n", + "reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)\n", + "\n", + "print(f\"Backpropagated {num_slices - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ae2d383c-c11c-457e-856f-85602f2df306", + "metadata": {}, + "source": [ + "The plots show that we did successfully backpropagate all 12 slices while keeping our observable under 10 commuting Pauli groups. We can also see that using `num_slices` along with `max_error_total` results in a distribution of budgets across the slices and unused budget is again forwarded to the next slice. This is most apparent in the **top right** plot, as the budget is both expended and replenished throughout backpropagation.\n", + "\n", + "It is notable that this method of distributing error produced the best results (more backpropagated slices), compared with the first experiment in this notebook and the example just previous this one. All of these examples allotted `.018` error budget, but backpropagation performed differently depending on how that budget was distributed." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b0bf5050-2928-41a1-9c3d-c9d79e918f6f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + }, + { + "cell_type": "markdown", + "id": "5c554774-2986-43a0-bdcd-45eaeb81ef5a", + "metadata": {}, + "source": [ + "## Work with multiple observables\n", + "\n", + "The `qiskit_addon_obp.backpropagate` method allows one to pass in a sequence of observables. This simplifies the workflow when dealing with multiple target observables.\n", + "\n", + "We explicitly mention this here to teach you how the truncation strategy handles such a case. For the sake of this example, we add an extra observable to the one which we have been using so far:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "0857ea4c-82c1-4641-a268-1a9ba44a312b", + "metadata": {}, + "outputs": [], + "source": [ + "obs = [SparsePauliOp(\"IIIIIZIIII\"), SparsePauliOp(\"IIIIIXIIII\")]" + ] + }, + { + "cell_type": "markdown", + "id": "ba3ac81c-7395-4287-907a-742a1751cbd0", + "metadata": {}, + "source": [ + "Finally, we repeat the first experiment from this tutorial., but with two observables to backpropagate the circuit into.\n", + "\n", + "For this example, this does not affect the number of slices that could be backpropagated. However, we can see that the two observables resulted in different numbers of Pauli terms and commuting groups." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "10c50f14-5bbd-427d-ae77-24389498a1e4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n" + ] + } + ], + "source": [ + "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n", + "print(truncation_error_budget)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "862edaec-fc2c-488b-875d-23841ecc80b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Backpropagated 11 circuit slices.\n", + "The new first observable contains 29 terms and 10 commuting groups.\n", + "The new second observable contains 23 terms and 8 commuting groups.\n" + ] + } + ], + "source": [ + "bp_obs, remaining_slices, metadata = backpropagate(\n", + " obs,\n", + " slices,\n", + " operator_budget=op_budget,\n", + " truncation_error_budget=truncation_error_budget,\n", + ")\n", + "reduced_circuit = combine_slices(remaining_slices)\n", + "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n", + "print(\n", + " f\"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")\n", + "print(\n", + " f\"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "467c614d-6bac-4826-a42b-39937b9d0c72", + "metadata": {}, + "source": [ + "The plots below help illustrate how the backpropagation algorithm handles multiple observables.\n", + "\n", + "First, the plots in the **top left**, **top right**, and **bottom left** show that the budget for truncating terms is set individually for each observable. In other words, both observables have the ability to truncate terms assuming an error of `0.001` per backpropagated slice.\n", + "Due to the different nature of the observables, this results in different consumption of the budget. In this example, they exhibit a lot of overlap, which does not always happen.\n", + "\n", + "The **bottom right** plot illustrates that `max_qwc_groups` takes _all_ observables into account. That means that the terms of all observables are grouped together to arrive at one final number of qubit-wise commuting groups, which is compared against `max_qwc_paulis`. The same is done for the `max_paulis` threshold (not discussed in this notebook), which allows you to set a limit on the number of Pauli terms across all observables." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n", + "plot_slice_errors(metadata, axes[(0, 0)])\n", + "plot_left_over_error_budget(metadata, axes[(0, 1)])\n", + "plot_accumulated_error(metadata, axes[(1, 0)])\n", + "plot_num_qwc_groups(metadata, axes[(1, 1)])" + ] + } + ], + "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": 5 +} diff --git a/docs/addons/qiskit-addon-obp/index.mdx b/docs/addons/qiskit-addon-obp/index.mdx new file mode 100644 index 00000000000..a00590b0ec6 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/index.mdx @@ -0,0 +1,31 @@ +--- +title: "Operator backpropagation (OBP)" +description: "Documentation for the latest version of Operator backpropagation (OBP)" +--- + +# Operator backpropagation (OBP) + +This package contains the Qiskit addon for operator backpropagation (OBP). Experimental errors limit the depth of quantum circuits that can be executed on near-term devices. OBP is a technique to reduce circuit depth by trimming operations from its end, at the cost of more operator measurements. + +As one backpropagates an operator further through a circuit, the size of the observable will grow exponentially, which results in both a classical and quantum resource overhead. However, for some circuits, the resulting distribution of Pauli observables is more concentrated than the worst-case exponential scaling, meaning that some terms in the Hamiltonian with small coefficients can be truncated to reduce the quantum overhead. The error incurred by doing this can be controlled to find a suitable tradeoff between precision and efficiency. + +There are several ways to perform operator backpropagation. This package uses a method based on Clifford perturbation theory, which has the benefit that the overhead incurred by backpropagating gates is determined by the non-Cliffordness of that gate. This leads to an increased efficiency for some families of circuits relative to tensor-network based methods for OBP, which currently have high classical overheads even in cases where the quantum overhead remains low. + +## Deprecation policy + +This package follows [semantic versioning](https://semver.org/) and is guided by the principles in [Qiskit’s deprecation policy](https://github.com/Qiskit/qiskit/blob/main/DEPRECATION.md). We may occasionally make breaking changes in order to improve the user experience. When possible, we will keep old interfaces and mark them as deprecated, as long as they can co-exist with the new ones. Each substantial improvement, breaking change, or deprecation will be documented in the [release notes](/docs/api/qiskit-addon-obp/release-notes). + +## Contributing + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-obp/blob/main/CONTRIBUTING.md) in the root of this project’s repository. By participating, you are expected to uphold Qiskit’s [code of conduct](https://github.com/Qiskit/qiskit/blob/main/CODE_OF_CONDUCT.md). + +We use [GitHub issues](https://github.com/Qiskit/qiskit-addon-obp/issues/new/choose) for tracking requests and bugs. + +## References + +1. 2. Fuller *et al.*, “[Improved quantum computation using operator backpropagation](https://www.nature.com/articles/s41534-026-01196-0),” *npj Quantum Inf.* **12**, 51 (2026). \[[arXiv](https://arxiv.org/abs/2502.01897)] + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-obp/blob/main/LICENSE.txt) + diff --git a/docs/addons/qiskit-addon-obp/install.mdx b/docs/addons/qiskit-addon-obp/install.mdx new file mode 100644 index 00000000000..3c543b968e1 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/install.mdx @@ -0,0 +1,77 @@ +--- +title: "Installation instructions" +description: "Installation instructions for the latest version of Operator backpropagation (OBP)" +--- + +# Installation instructions + +Before installing the package, choose how you’re going to run and install the packages. There are two primary ways to do this: + +* [Option 1: Install from PyPI](#option-1) +* [Option 2: Install from source](#option-2) + +## Preinstallation + +First, create a minimal environment with only Python installed in it. We recommend using [Python virtual environments](https://docs.python.org/3.10/tutorial/venv.html). + +```sh +python3 -m venv /path/to/virtual/environment +``` + +Activate your new environment. + +```sh +source /path/to/virtual/environment/bin/activate +``` + +Note: If you are using Windows, use the following commands in PowerShell: + +```pwsh +python3 -m venv c:\path\to\virtual\environment +c:\path\to\virtual\environment\Scripts\Activate.ps1 +``` + + + +## Option 1: Install from PyPI + +The most straightforward way to install the `qiskit-addon-obp` package is by using `PyPI`. + +```sh +pip install 'qiskit-addon-obp' +``` + + + +## Option 2: Install from source + +Users who want to develop in the repository or run the notebooks locally might want to install from source. + +If so, the first step is to clone the `qiskit-addon-obp` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-obp.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-obp +``` + +The next step is to install `qiskit-addon-obp` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies so you can run visualizations. If you plan on developing in the repository, you might want to install the `dev` dependencies. + +Adjust the options below to suit your needs. + +```sh +pip install tox notebook -e '.[notebook-dependencies,dev]' +``` + +If you installed the notebook dependencies, you can get started by running the notebooks in the docs directory. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/api/qiskit-addon-obp/_toc.json b/docs/api/qiskit-addon-obp/_toc.json index e2bd0263c41..3336d5e5e49 100644 --- a/docs/api/qiskit-addon-obp/_toc.json +++ b/docs/api/qiskit-addon-obp/_toc.json @@ -72,5 +72,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-obp", + "parentLabel": "Operator backpropagation (OBP)" } diff --git a/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx b/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx index 0dc24a61be8..1d45bc3986e 100644 --- a/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx +++ b/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx @@ -18,7 +18,7 @@ Main operator backpropagation functionality. ### backpropagate - + Backpropagate slices of quantum circuit operations onto the provided observables. This function takes a (list of) observable(s) and backpropagates the provided quantum circuit slices **in reverse order** onto the observable(s) until one of the stopping criteria is reached. @@ -34,7 +34,7 @@ Main operator backpropagation functionality. **Parameters** * **observables** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)*]*) – The observable(s) onto which the circuit is backpropagated. - * **slices** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)*]*) – A sequence of `QuantumCircuit` objects representing a single circuit which has been separated into partitions spanning all qubits. **These “slices” will be backpropagated in reverse order.** Each slice must span all qubits. One may use the tools provided in [`qiskit_addon_utils.slicing`](https://qiskit.github.io/qiskit-addon-utils/apidocs/qiskit_addon_utils.slicing.html#module-qiskit_addon_utils.slicing "(in Qiskit addon utilities)") to slice a single [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit). + * **slices** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)*]*) – A sequence of `QuantumCircuit` objects representing a single circuit which has been separated into partitions spanning all qubits. **These “slices” will be backpropagated in reverse order.** Each slice must span all qubits. One may use the tools provided in [`qiskit_addon_utils.slicing`](/docs/api/qiskit-addon-utils/slicing#module-qiskit_addon_utils.slicing#module-qiskit_addon_utils.slicing "(in Qiskit addon utilities)") to slice a single [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit). * **truncation\_error\_budget** ([*TruncationErrorBudget*](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") *| None*) – The error budget used for truncating Pauli terms. Refer to the [how-to guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) for a detailed discussion on truncating terms from the output operator and bounding the incurred error. * **operator\_budget** ([*OperatorBudget*](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") *| None*) – Constraints on how large the operator may grow during backpropagation. If `None`, a default instance of [`OperatorBudget`](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") will be used, and no constraints will be placed on the output operator size. * **max\_seconds** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – The maximum number of seconds to run the backpropagation. If this timeout is triggered before the function returns, the metadata of that moment will be returned. Note, that this metadata may contain only partial information for the last slice being backpropagated. @@ -54,6 +54,6 @@ Main operator backpropagation functionality. **Return type** - [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)], [*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)], [*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")] + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)], [*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)], [*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")] diff --git a/docs/api/qiskit-addon-obp/release-notes.mdx b/docs/api/qiskit-addon-obp/release-notes.mdx index 2ef507a6319..6af107d322b 100644 --- a/docs/api/qiskit-addon-obp/release-notes.mdx +++ b/docs/api/qiskit-addon-obp/release-notes.mdx @@ -10,6 +10,18 @@ in_page_toc_max_heading_level: 2 # Operator backpropagation (OBP) release notes + + + + +## Upcoming release (`136/merge`) + + + +### Upgrade Notes + +* Support for Python 3.9 has been removed. The minimum supported Python version is now 3.10. + @@ -20,13 +32,13 @@ in_page_toc_max_heading_level: 2 ### New Features -* [`PauliLindbladError`](/docs/api/qiskit-ibm-runtime/utils-noise-learner-result-pauli-lindblad-error "(in Qiskit Runtime IBM Client)") objects can now be embedded in slices of `QuantumCircuit` using the [`PauliLindbladErrorInstruction`](utils-noise-pauli-lindblad-error-instruction "qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction") and will be handled properly by [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate"). +* [`PauliLindbladError`](/docs/api/qiskit-ibm-runtime/utils-noise-learner-result-pauli-lindblad-error "(in Qiskit Runtime IBM Client)") objects can now be embedded in slices of `QuantumCircuit` using the [`PauliLindbladErrorInstruction`](/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction "qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction") and will be handled properly by [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate"). ### Bug Fixes -* Fixed a bug in [`qiskit_addon_obp.backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") which often caused resets to be applied to the wrong qubit. +* Fixed a bug in [`qiskit_addon_obp.backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") which often caused resets to be applied to the wrong qubit. @@ -40,16 +52,18 @@ in_page_toc_max_heading_level: 2 ### New Features -* A new parameter `show_legend` has been added to each function in the [`qiskit_addon_obp.utils.visualization`](utils-visualization#module-qiskit_addon_obp.utils.visualization "qiskit_addon_obp.utils.visualization") module that can show or hide the legend on a plot. The legend is shown by default. This can be useful when the legend becomes long and obstructs the plot. +* A new parameter `show_legend` has been added to each function in the [`qiskit_addon_obp.utils.visualization`](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization "qiskit_addon_obp.utils.visualization") module that can show or hide the legend on a plot. The legend is shown by default. This can be useful when the legend becomes long and obstructs the plot. -* [`qiskit_addon_obp.utils.simplify.OperatorBudget`](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") now holds `atol` and `rtol` fields which are absolute and relative tolerances used to determine whether coefficients are zero while simplifying an operator. +* [`qiskit_addon_obp.utils.simplify.OperatorBudget`](/docs/api/qiskit-addon-obp/utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") now holds `atol` and `rtol` fields which are absolute and relative tolerances used to determine whether coefficients are zero while simplifying an operator. -* [`truncate_binary_search()`](utils-truncating#truncate_binary_search "qiskit_addon_obp.utils.truncating.truncate_binary_search") now accepts a `tol` kwarg. Once an optimal truncation threshold, up to this value, has been found, the search for an optimal threshold will stop. The default tolerance is `1e-8`; whereas, the tolerance used prior to this release was `1e-10`. +* [`truncate_binary_search()`](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search "qiskit_addon_obp.utils.truncating.truncate_binary_search") now accepts a `tol` kwarg. Once an optimal truncation threshold, up to this value, has been found, the search for an optimal threshold will stop. The default tolerance is `1e-8`; whereas, the tolerance used prior to this release was `1e-10`. - [`TruncationErrorBudget`](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") now holds a `tol` field. This field is used by [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") as the `tol` argument to [`truncate_binary_search()`](utils-truncating#truncate_binary_search "qiskit_addon_obp.utils.truncating.truncate_binary_search"). + [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") now holds a `tol` field. This field is used by [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") as the `tol` argument to [`truncate_binary_search()`](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search "qiskit_addon_obp.utils.truncating.truncate_binary_search"). + + ### Upgrade Notes * This release adds support for Python 3.13. No code changes were necessary, so older releases are expected to work on Python 3.13 too. @@ -58,43 +72,43 @@ in_page_toc_max_heading_level: 2 - + ### Bug Fixes -* The [`num_duplicate_paulis`](utils-simplify#num_duplicate_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_duplicate_paulis") was previously unable to differentiate from Pauli terms that were trimmed due to their coefficient being close to zero. This is now tracked correctly with these trimmed terms only counting towards [`num_trimmed_paulis`](utils-simplify#num_trimmed_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_trimmed_paulis"). +* The [`num_duplicate_paulis`](/docs/api/qiskit-addon-obp/utils-simplify#num_duplicate_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_duplicate_paulis") was previously unable to differentiate from Pauli terms that were trimmed due to their coefficient being close to zero. This is now tracked correctly with these trimmed terms only counting towards [`num_trimmed_paulis`](/docs/api/qiskit-addon-obp/utils-simplify#num_trimmed_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_trimmed_paulis"). -* The reported number of unique Paulis in [`num_unique_paulis`](utils-simplify#num_unique_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_unique_paulis") is now correct in cases where all gates in a slice commute with an observable. +* The reported number of unique Paulis in [`num_unique_paulis`](/docs/api/qiskit-addon-obp/utils-simplify#num_unique_paulis "qiskit_addon_obp.utils.simplify.SimplifyMetadata.num_unique_paulis") is now correct in cases where all gates in a slice commute with an observable. -* Fixed a bug in [`qiskit_addon_obp.backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") which caused slices with depth > 1 to be incorrectly forward-propagated. Gates from a given slice will now be correctly propagated into the observable in reverse order (i.e., from the back of the slice). +* Fixed a bug in [`qiskit_addon_obp.backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") which caused slices with depth > 1 to be incorrectly forward-propagated. Gates from a given slice will now be correctly propagated into the observable in reverse order (i.e., from the back of the slice). - + ## 0.1.0 - + ### New Features -* Added a [`OperatorBudget`](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") class for specifying how large an operator may grow during back-propagation. +* Added a [`OperatorBudget`](/docs/api/qiskit-addon-obp/utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") class for specifying how large an operator may grow during back-propagation. -* Adds the `max_seconds` keyword-argument to the [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") function, allowing the end-user to specify a maximum wall clock time for the algorithm. This can (for example) be useful for exploring different truncation error budget strategies while limiting the CPU time. +* Adds the `max_seconds` keyword-argument to the [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") function, allowing the end-user to specify a maximum wall clock time for the algorithm. This can (for example) be useful for exploring different truncation error budget strategies while limiting the CPU time. -* Introduced a new `dataclass`, [`TruncationErrorBudget`](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget"), for holding information about the observable truncation strategy. +* Introduced a new `dataclass`, [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget"), for holding information about the observable truncation strategy. -* Introduced a new function, [`setup_budget()`](utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget"), which generates a [`TruncationErrorBudget`](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") class, given an observable truncation strategy (e.g. `max_error_total`, `max_error_per_slice`, `p_norm`). +* Introduced a new function, [`setup_budget()`](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget"), which generates a [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") class, given an observable truncation strategy (e.g. `max_error_total`, `max_error_per_slice`, `p_norm`). - + ### Upgrade Notes -* The [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") function no longer accepts `max_paulis` and `max_qwc_groups` kwargs for constraining the size of the operator during back-propagation. Users should instead use the new `operator_budget` kwarg, which takes an [`OperatorBudget`](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") instance. +* The [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") function no longer accepts `max_paulis` and `max_qwc_groups` kwargs for constraining the size of the operator during back-propagation. Users should instead use the new `operator_budget` kwarg, which takes an [`OperatorBudget`](/docs/api/qiskit-addon-obp/utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget") instance. To migrate, change this code @@ -120,7 +134,7 @@ in_page_toc_max_heading_level: 2 bp_obs, remaining_slices, metadata = backpropagate(obs, slices, operator_budget=op_budget) ``` -* The `max_slices` kwarg has been removed from [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate"). Users should now only pass in slices which they intend to back-propagate. If a user wants to attempt to only back-propagate the last `20` slices of an `N`-slice circuit, they would simply pass in the last `20` slices to [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") and, recombine any slices remaining after back-propagation with the original `N-20` slices. +* The `max_slices` kwarg has been removed from [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate"). Users should now only pass in slices which they intend to back-propagate. If a user wants to attempt to only back-propagate the last `20` slices of an `N`-slice circuit, they would simply pass in the last `20` slices to [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") and, recombine any slices remaining after back-propagation with the original `N-20` slices. For example @@ -137,9 +151,9 @@ in_page_toc_max_heading_level: 2 reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices) ``` -* The `max_slices` kwarg in [`setup_budget()`](utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget") has been renamed to `num_slices`. +* The `max_slices` kwarg in [`setup_budget()`](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget") has been renamed to `num_slices`. -* The `max_slices` attribute in [`OBPMetadata`](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") has been renamed to `num_slices`. +* The `max_slices` attribute in [`OBPMetadata`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") has been renamed to `num_slices`. * The project’s root Python namespace has been changed from `obp` to `qiskit_addon_obp`. All package imports must be updated. @@ -155,19 +169,19 @@ in_page_toc_max_heading_level: 2 from qiskit_addon_obp import backpropagate ``` -* Removed the `max_error_total`, `max_error_per_slice`, and `p_norm` kwargs from the [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") signature. Instead, users must specify their observable truncation strategy with the new `truncation_error_budget` kwarg which accepts a [`TruncationErrorBudget`](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") instance. +* Removed the `max_error_total`, `max_error_per_slice`, and `p_norm` kwargs from the [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") signature. Instead, users must specify their observable truncation strategy with the new `truncation_error_budget` kwarg which accepts a [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") instance. -* Removed the `per_slice_budget`, `max_error_total`, and `p_norm` fields from the [`OBPMetadata`](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") class. These fields will now be accessed through the new `truncation_error_budget` field, which holds a [`TruncationErrorBudget`](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") instance. +* Removed the `per_slice_budget`, `max_error_total`, and `p_norm` fields from the [`OBPMetadata`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") class. These fields will now be accessed through the new `truncation_error_budget` field, which holds a [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") instance. - + ### Bug Fixes -* The [`setup_budget()`](utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget") erroneously distributed the `max_error_total` when `num_slices` was also set. This has been fixed now, such that the budget always gets distributed evenly, regardless of the value of `p_norm`. +* The [`setup_budget()`](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget "qiskit_addon_obp.utils.truncating.setup_budget") erroneously distributed the `max_error_total` when `num_slices` was also set. This has been fixed now, such that the budget always gets distributed evenly, regardless of the value of `p_norm`. -* When the `max_seconds` argument to the [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") method is used, but the timeout is not reached during the actual OBP execution, the signal will now be reset properly, thereby avoiding cancellations at a (seemingly) random later point in time (of course, it is not random but actually after the specified amount of time has passed, but the rest of the code being executed after OBP could be doing anything at this point). +* When the `max_seconds` argument to the [`backpropagate()`](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") method is used, but the timeout is not reached during the actual OBP execution, the signal will now be reset properly, thereby avoiding cancellations at a (seemingly) random later point in time (of course, it is not random but actually after the specified amount of time has passed, but the rest of the code being executed after OBP could be doing anything at this point). -* The computation of the [`accumulated_error()`](utils-metadata-obp-metadata#accumulated_error "qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error") and [`left_over_error_budget()`](utils-metadata-obp-metadata#left_over_error_budget "qiskit_addon_obp.utils.metadata.OBPMetadata.left_over_error_budget") were fixed to respect the [Minkowski inequality](https://en.wikipedia.org/wiki/Minkowski_inequality). This is necessary, because a general Lp-norm (other than `p=2`) does not satisfy the [parallelogram law](https://en.wikipedia.org/wiki/Parallelogram_law) which resulted in a non-rigorous upper bound of the actual accumulated errors (and left-over error budgets by extension). +* The computation of the [`accumulated_error()`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata#accumulated_error "qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error") and [`left_over_error_budget()`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata#left_over_error_budget "qiskit_addon_obp.utils.metadata.OBPMetadata.left_over_error_budget") were fixed to respect the [Minkowski inequality](https://en.wikipedia.org/wiki/Minkowski_inequality). This is necessary, because a general Lp-norm (other than `p=2`) does not satisfy the [parallelogram law](https://en.wikipedia.org/wiki/Parallelogram_law) which resulted in a non-rigorous upper bound of the actual accumulated errors (and left-over error budgets by extension). diff --git a/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx b/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx index 59e391628c9..b90bc50222a 100644 --- a/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx +++ b/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx @@ -8,7 +8,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata # OBPMetadata - + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) A container for metadata generated during the `backpropagate()` method. @@ -17,9 +17,9 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata **Parameters** - * **truncation\_error\_budget** ([*TruncationErrorBudget*](utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget")) + * **truncation\_error\_budget** ([*TruncationErrorBudget*](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget")) * **num\_slices** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) - * **operator\_budget** ([*OperatorBudget*](utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget")) + * **operator\_budget** ([*OperatorBudget*](/docs/api/qiskit-addon-obp/utils-simplify#operatorbudget "qiskit_addon_obp.utils.simplify.OperatorBudget")) * **backpropagation\_history** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*SliceMetadata*](utils-metadata-slice-metadata "qiskit_addon_obp.utils.metadata.SliceMetadata")*]*) * **num\_backpropagated\_slices** ([*int*](https://docs.python.org/3/library/functions.html#int)) @@ -61,12 +61,12 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata ### accumulated\_error - + Compute the accumulated error for a given observable at a given “time”. This method computes the accumulated error for a given observable index at a given “time” during the course of the backpropagation. In this context, “time” is to be understood as the discrete steps of already backpropagated slices. - The accumulated error is computed as the sum of the individual [`SliceMetadata.slice_errors`](utils-metadata-slice-metadata#slice_errors "qiskit_addon_obp.utils.metadata.SliceMetadata.slice_errors"). These in turn may be computed within a specified [`TruncationErrorBudget.p_norm`](utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm"). Thus, the computed accumulated error is an upper bound to the real accumulated error as given by the [Minkowski inequality](https://en.wikipedia.org/wiki/Minkowski_inequality) (the generalization of the triangle inequality for Lp-norms other than `p=2`). + The accumulated error is computed as the sum of the individual [`SliceMetadata.slice_errors`](utils-metadata-slice-metadata#slice_errors "qiskit_addon_obp.utils.metadata.SliceMetadata.slice_errors"). These in turn may be computed within a specified [`TruncationErrorBudget.p_norm`](/docs/api/qiskit-addon-obp/utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm"). Thus, the computed accumulated error is an upper bound to the real accumulated error as given by the [Minkowski inequality](https://en.wikipedia.org/wiki/Minkowski_inequality) (the generalization of the triangle inequality for Lp-norms other than `p=2`). Since a general Lp-norm (other than `p=2`) is *not* an inner product norm, it does *not* satisfy the [parallelogram law](https://en.wikipedia.org/wiki/Parallelogram_law). Hence, we must use the Minkowski inequality as the upper bound of the accumulated error. @@ -88,7 +88,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata ### from\_json - + Load a metadata from a json file. **Parameters** @@ -106,12 +106,12 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata ### left\_over\_error\_budget - + Compute the left-over error budget for a given observable at a given “time”. This method computes the left-over error budget for a given observable index at a given “time” during the course of the backpropagation. In this context, “time” is to be understood as the discrete steps of already backpropagated slices. - The left-over error budget is computed as the remainder of the total budget minus the sum of the individual [`SliceMetadata.slice_errors`](utils-metadata-slice-metadata#slice_errors "qiskit_addon_obp.utils.metadata.SliceMetadata.slice_errors"). These in turn may be computed within a specified [`TruncationErrorBudget.p_norm`](utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm"). + The left-over error budget is computed as the remainder of the total budget minus the sum of the individual [`SliceMetadata.slice_errors`](utils-metadata-slice-metadata#slice_errors "qiskit_addon_obp.utils.metadata.SliceMetadata.slice_errors"). These in turn may be computed within a specified [`TruncationErrorBudget.p_norm`](/docs/api/qiskit-addon-obp/utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm"). See also the explanations in [`accumulated_error()`](#qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error "qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error") for more details on how the individual slice errors are summed up to form an upper bound to the real error via the Minkowski inequality. @@ -137,7 +137,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata ### to\_json - + Dump this metadata to a json file. **Parameters** diff --git a/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx b/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx index 2c3517d4932..4d7cc2dd7e4 100644 --- a/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx +++ b/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx @@ -8,7 +8,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.SliceMetadata # SliceMetadata - + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) A container for metadata generated during the backpropagation of a single slice. @@ -76,7 +76,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.SliceMetadata The sum of the coefficients for each observable that were trimmed during operator simplification because each individual coefficient was below the trimming threshold. - This sum is *not* affected by the value of [`p_norm`](utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm")! + This sum is *not* affected by the value of [`p_norm`](/docs/api/qiskit-addon-obp/utils-truncating#p_norm "qiskit_addon_obp.utils.truncating.TruncationErrorBudget.p_norm")! diff --git a/docs/api/qiskit-addon-obp/utils-metadata.mdx b/docs/api/qiskit-addon-obp/utils-metadata.mdx index 4375fd5ef91..20e6828efd2 100644 --- a/docs/api/qiskit-addon-obp/utils-metadata.mdx +++ b/docs/api/qiskit-addon-obp/utils-metadata.mdx @@ -16,8 +16,8 @@ python_api_name: qiskit_addon_obp.utils.metadata Container classes for holding backpropagation metadata. -| | | -| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | -| [`OBPMetadata`](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") | A container for metadata generated during the `backpropagate()` method. | -| [`SliceMetadata`](utils-metadata-slice-metadata "qiskit_addon_obp.utils.metadata.SliceMetadata") | A container for metadata generated during the backpropagation of a single slice. | +| | | +| --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| [`OBPMetadata`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata") | A container for metadata generated during the `backpropagate()` method. | +| [`SliceMetadata`](/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata "qiskit_addon_obp.utils.metadata.SliceMetadata") | A container for metadata generated during the backpropagation of a single slice. | diff --git a/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction.mdx b/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction.mdx index f92f265d8ba..2484d2ebefd 100644 --- a/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction.mdx +++ b/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction.mdx @@ -8,7 +8,7 @@ python_api_name: qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction # PauliLindbladErrorInstruction - + Bases: [`Instruction`](/docs/api/qiskit/qiskit.circuit.Instruction) A lightweight wrapper around a `PauliLindbladError`. @@ -177,7 +177,7 @@ python_api_name: qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction ### repeat - Creates an instruction with `self` repeated :math\`n\` times. + Creates an instruction with `self` repeated $n$ times. **Parameters** @@ -243,7 +243,7 @@ python_api_name: qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction ### validate\_parameter - Instruction parameters has no validation or normalization. + Instruction parameter has no validation or normalization. diff --git a/docs/api/qiskit-addon-obp/utils-noise.mdx b/docs/api/qiskit-addon-obp/utils-noise.mdx index c09109eb726..d37043bbe3d 100644 --- a/docs/api/qiskit-addon-obp/utils-noise.mdx +++ b/docs/api/qiskit-addon-obp/utils-noise.mdx @@ -16,7 +16,7 @@ python_api_name: qiskit_addon_obp.utils.noise Utilities for noise operators. -| | | -| -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| [`PauliLindbladErrorInstruction`](utils-noise-pauli-lindblad-error-instruction "qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction") | A lightweight wrapper around a `PauliLindbladError`. | +| | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| [`PauliLindbladErrorInstruction`](/docs/api/qiskit-addon-obp/utils-noise-pauli-lindblad-error-instruction "qiskit_addon_obp.utils.noise.PauliLindbladErrorInstruction") | A lightweight wrapper around a `PauliLindbladError`. | diff --git a/docs/api/qiskit-addon-obp/utils-operations.mdx b/docs/api/qiskit-addon-obp/utils-operations.mdx index 3c4945b8f86..9acd121d958 100644 --- a/docs/api/qiskit-addon-obp/utils-operations.mdx +++ b/docs/api/qiskit-addon-obp/utils-operations.mdx @@ -18,7 +18,7 @@ Utility functions for operator backpropagation. ### apply\_op\_to - + Apply the operator `op2` to the operator `op1`. These operators do not necessarily need to act on the same number of qubits, as they are assumed to act on a larger system. The position in the system of each operator is defined by the corresponding `qargs`. The output operator will be defined on `union(op1_qargs, op2_qargs)`. @@ -56,7 +56,7 @@ Utility functions for operator backpropagation. ### apply\_reset\_to - + Apply a reset operation to a Pauli operator. This function applies a reset operation to `op` in the following way: @@ -82,7 +82,7 @@ Utility functions for operator backpropagation. ### to\_global\_op - + Convert a local operator to a global operator by inserting identities on qubits which aren’t used. **Parameters** @@ -106,7 +106,7 @@ Utility functions for operator backpropagation. ### reduce\_op - + Create a lean representation of a global Pauli operator. This function returns a lean representation of the input operator such that all of the qubits associated solely with Pauli-I terms have been removed. A list of indices is also returned indicating on which qubits the lean operator acts. diff --git a/docs/api/qiskit-addon-obp/utils-simplify.mdx b/docs/api/qiskit-addon-obp/utils-simplify.mdx index 467b9568aff..7f385078694 100644 --- a/docs/api/qiskit-addon-obp/utils-simplify.mdx +++ b/docs/api/qiskit-addon-obp/utils-simplify.mdx @@ -18,7 +18,7 @@ Functions for simplifying Pauli operators. ### OperatorBudget - + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) A class for storing the constants that determine how large an operator may grow. @@ -44,7 +44,7 @@ Functions for simplifying Pauli operators. #### is\_active - + Return whether `self` places any bounds on operator size. **Return type** @@ -79,7 +79,7 @@ Functions for simplifying Pauli operators. ### SimplifyMetadata - + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) A simple dataclass for returning the tracked attributes during operator simplification. @@ -118,7 +118,7 @@ Functions for simplifying Pauli operators. ### simplify - + Simplifies the provided Pauli operator. This is an adaption of `SparsePauliOp.simplify()` which tracks metadata of the simplified terms. diff --git a/docs/api/qiskit-addon-obp/utils-truncating.mdx b/docs/api/qiskit-addon-obp/utils-truncating.mdx index 2e6b97735e1..53815adf710 100644 --- a/docs/api/qiskit-addon-obp/utils-truncating.mdx +++ b/docs/api/qiskit-addon-obp/utils-truncating.mdx @@ -18,7 +18,7 @@ Functions for truncating Pauli operators within given error budgets. ### TruncationErrorBudget - + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) A class for storing the constants that determine the truncation error budget. @@ -34,7 +34,7 @@ Functions for truncating Pauli operators within given error budgets. #### is\_active - + Return whether the truncation is active, i.e. whether the budget is non-zero. **Return type** @@ -71,7 +71,7 @@ Functions for truncating Pauli operators within given error budgets. ### setup\_budget - + Calculate the budget available to each slice for observable term truncation. This method makes the construction of a [`TruncationErrorBudget`](#qiskit_addon_obp.utils.truncating.TruncationErrorBudget "qiskit_addon_obp.utils.truncating.TruncationErrorBudget") easier for an end-user. This error budget can be provided to the [`backpropagate()`](qiskit-addon-obp#backpropagate "qiskit_addon_obp.backpropagate") method to enable the truncation of low-weight Pauli terms. Refer to the [how-to guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) for a detailed discussion on truncating terms from the output operator and bounding the incurred error. @@ -111,7 +111,7 @@ Functions for truncating Pauli operators within given error budgets. ### truncate\_binary\_search - + Perform binary search to find an optimal observable truncation threshold. Removes the Pauli terms from a `SparsePauliOp` whose sum of their absolute coefficients values does not exceed the provided error `budget`. @@ -119,7 +119,7 @@ Functions for truncating Pauli operators within given error budgets. **Parameters** * **observable** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – the `SparsePauliOp` to truncate terms from. - * **budget** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the maximum permissable truncation error. + * **budget** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the maximum permissible truncation error. * **p\_norm** ([*int*](https://docs.python.org/3/library/functions.html#int)) – an integer specifying what p-norm to use. * **tol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – when the binary search thresholds differ by an amount smaller than `tol`, the threshold search will stop. diff --git a/docs/api/qiskit-addon-obp/utils-visualization.mdx b/docs/api/qiskit-addon-obp/utils-visualization.mdx index f21a5e38145..af549522ea3 100644 --- a/docs/api/qiskit-addon-obp/utils-visualization.mdx +++ b/docs/api/qiskit-addon-obp/utils-visualization.mdx @@ -18,10 +18,10 @@ Various visualization utilities. ### plot\_accumulated\_error - + Plot the accumulated error. - This method populates the provided figure axes with a line-plot of the [`OBPMetadata.accumulated_error()`](utils-metadata-obp-metadata#accumulated_error "qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error"). Below is an example where we plot some `metadata` which exists within our context. + This method populates the provided figure axes with a line-plot of the [`OBPMetadata.accumulated_error()`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata#accumulated_error "qiskit_addon_obp.utils.metadata.OBPMetadata.accumulated_error"). Below is an example where we plot some `metadata` which exists within our context. ```python >>> from matplotlib import pyplot as plt @@ -38,7 +38,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -49,10 +49,10 @@ Various visualization utilities. ### plot\_left\_over\_error\_budget - + Plot the left-over error budget. - This method populates the provided figure axes with a line-plot of the [`OBPMetadata.left_over_error_budget()`](utils-metadata-obp-metadata#left_over_error_budget "qiskit_addon_obp.utils.metadata.OBPMetadata.left_over_error_budget"). Below is an example where we plot some `metadata` which exists within our context. + This method populates the provided figure axes with a line-plot of the [`OBPMetadata.left_over_error_budget()`](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata#left_over_error_budget "qiskit_addon_obp.utils.metadata.OBPMetadata.left_over_error_budget"). Below is an example where we plot some `metadata` which exists within our context. ```python >>> from matplotlib import pyplot as plt @@ -67,7 +67,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -78,7 +78,7 @@ Various visualization utilities. ### plot\_slice\_errors - + Plot the slice errors. This method populates the provided figure axes with a bar-plot of the truncation error incurred at each backpropagated slice. Below is an example where we plot some `metadata` which exists within our context. @@ -98,7 +98,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -109,7 +109,7 @@ Various visualization utilities. ### plot\_num\_paulis - + Plot the number of Pauli terms. This method populates the provided figure axes with a line-plot of the number of Pauli terms at each backpropagated slice. Below is an example where we plot some `metadata` which exists within our context. @@ -129,7 +129,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -140,7 +140,7 @@ Various visualization utilities. ### plot\_num\_truncated\_paulis - + Plot the number of truncated Pauli terms. This method populates the provided figure axes with a bar-plot of the number of the truncated Pauli terms at each backpropagated slice. Below is an example where we plot some `metadata` which exists within our context. @@ -160,7 +160,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -171,7 +171,7 @@ Various visualization utilities. ### plot\_sum\_paulis - + Plot the total number of all Pauli terms. This method populates the provided figure axes with a line-plot of the total number of all Pauli terms at each backpropagated slice. Below is an example where we plot some `metadata` which exists within our context. @@ -191,7 +191,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. @@ -202,7 +202,7 @@ Various visualization utilities. ### plot\_num\_qwc\_groups - + Plot the number of qubit-wise commuting Pauli groups. This method populates the provided figure axes with a line-plot of the number of qubit-wise commuting Pauli groups at each backpropagated slice. Below is an example where we plot some `metadata` which exists within our context. @@ -220,7 +220,7 @@ Various visualization utilities. **Parameters** - * **metadata** ([*OBPMetadata*](utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. + * **metadata** ([*OBPMetadata*](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata "qiskit_addon_obp.utils.metadata.OBPMetadata")) – the metadata to be visualized. * **axes** (*Axes*) – the matplotlib axes in which to plot. * **show\_legend** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – enable/disable showing the legend in the plot. diff --git a/public/docs/api/qiskit-addon-obp/objects.inv b/public/docs/api/qiskit-addon-obp/objects.inv index c5791a33f25..d601ca6ddb4 100644 Binary files a/public/docs/api/qiskit-addon-obp/objects.inv and b/public/docs/api/qiskit-addon-obp/objects.inv differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/d6f281d4-706e-41ad-b7b5-bc7ebe106996-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/d6f281d4-706e-41ad-b7b5-bc7ebe106996-0.avif new file mode 100644 index 00000000000..1f66445a312 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/d6f281d4-706e-41ad-b7b5-bc7ebe106996-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/fe7f454c-7d79-4c61-b1dd-6a98564fb5f1-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/fe7f454c-7d79-4c61-b1dd-6a98564fb5f1-0.avif new file mode 100644 index 00000000000..069e3cfbcbd Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/fe7f454c-7d79-4c61-b1dd-6a98564fb5f1-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/ff72e77f-e2c0-4cce-8575-c5aa587f78fb-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/ff72e77f-e2c0-4cce-8575-c5aa587f78fb-0.avif new file mode 100644 index 00000000000..722ad405b96 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/ff72e77f-e2c0-4cce-8575-c5aa587f78fb-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/8758f393-8691-4116-94ca-1185c6188bb4-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/8758f393-8691-4116-94ca-1185c6188bb4-0.avif new file mode 100644 index 00000000000..c6f6d183e97 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/8758f393-8691-4116-94ca-1185c6188bb4-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/dadc314d-452f-465c-a382-d54685ce3f2a-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/dadc314d-452f-465c-a382-d54685ce3f2a-0.avif new file mode 100644 index 00000000000..a7f9f56ec92 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/simulating-circuits-with-obp/extracted-outputs/dadc314d-452f-465c-a382-d54685ce3f2a-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif new file mode 100644 index 00000000000..1f66445a312 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif new file mode 100644 index 00000000000..d7759b6a948 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif new file mode 100644 index 00000000000..2619a764523 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif new file mode 100644 index 00000000000..518f1a7f9fc Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif new file mode 100644 index 00000000000..acab16b05d8 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif new file mode 100644 index 00000000000..04a0c5eda7b Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif new file mode 100644 index 00000000000..e417e70524d Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif new file mode 100644 index 00000000000..5b4f58f8b26 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-10.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-10.avif index 7400e97e1b8..406f193ef56 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-10.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-10.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-12.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-12.avif index 42178152132..91f24887429 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-12.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-12.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-14.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-14.avif index 73e9ef64255..d126a4d7f92 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-14.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-14.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-2.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-2.avif index 248ca15aa5f..7cc0233c6ed 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-2.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-2.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-4.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-4.avif index 0c4991e782e..6085b852f82 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-4.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-4.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-6.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-6.avif index a1b8aa55dff..9c0e343c812 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-6.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-6.avif differ diff --git a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-8.avif b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-8.avif index ce2d163fbb5..15c5864f9a3 100644 Binary files a/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-8.avif and b/public/docs/images/api/qiskit-addon-obp/qiskit_addon_obp-utils-visualization-8.avif differ diff --git a/scripts/config/historical-pages-to-latest.json b/scripts/config/historical-pages-to-latest.json index 2ec9c560f1e..7ca6c4fa0fc 100644 --- a/scripts/config/historical-pages-to-latest.json +++ b/scripts/config/historical-pages-to-latest.json @@ -1483,6 +1483,17 @@ "0.16": {}, "0.17": {} }, + "qiskit-c": { + "2.0": {}, + "2.1": {}, + "2.2": { + "qk-vf-2-layout-result": "/" + }, + "2.3": {}, + "dev": { + "qk-param": "/" + } + }, "qiskit-addon-aqc-tensor": { "0.1": {}, "0.2": {} @@ -1510,16 +1521,5 @@ "0.2": { "noise-management-post-selection": "/" } - }, - "qiskit-c": { - "2.0": {}, - "2.1": {}, - "2.2": { - "qk-vf-2-layout-result": "/" - }, - "2.3": {}, - "dev": { - "qk-param": "/" - } } }