diff --git a/README.md b/README.md index 1b902c9a824..1cbc5a3a244 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,13 @@ API docs authors can preview their changes to one of the APIs by using the `-a` 1. Run `npm run gen-api -- -p -v -a `. 2. Execute `./start` and open up `http://localhost:3000`, as explained in the prior section. +### Addon docs authors: How to preview your changes + +Addon docs authors can preview guide and how-to changes by pointing the addon pipeline at a local Sphinx build: + +1. Run `npm run gen-addon -- -p --sphinx-artifact-folder `. +2. Execute `./start` and navigate to `http://localhost:3000/docs/addons/`. + ## Run quality checks We use multiple tools to ensure that documentation meets high standards. These tools will run automatically in your PR through CI, but it is much faster to run the checks locally. @@ -320,6 +327,36 @@ Additionally, If you are regenerating a dev version, then you can add `--dev` as In this case, no commit will be automatically created. +## Generate or update addon docs + +Addon docs (guides, how-tos, explanations, and notebook-based tutorials) for packages like `qiskit-addon-cutting`, `qiskit-addon-obp`, `pauli-prop`, etc., are generated by a separate pipeline from the API stubs pipeline. Content is published to `docs/addons/{pkg}/` rather than `docs/api/{pkg}/`. + +### Generate addon docs for a new release + +1. Download the Sphinx HTML artifact from the package's GitHub Actions workflow (see the [CI artifact links](#initial-steps) in the API docs section). +2. Rename the zip to its minor version, e.g. `0.5.zip`, and upload it to the Box folder. +3. Run: + ```sh + npm run gen-addon -- -p + ``` + For example: + ```sh + npm run gen-addon -- -p qiskit-addon-cutting + npm run gen-addon -- -p pauli-prop + ``` + To regenerate all addon packages at once: + ```sh + npm run gen-addon -- --all + ``` +4. Open a pull request with the generated changes. + +### Add a new addon package + +1. Add the package name to `Pkg.ADDON_NAMES` in `scripts/js/lib/api/Pkg.ts` and add a `fromArgs` branch that returns a `new Pkg(...)` with the correct `title`, `githubSlug`, and `language`. +2. Add the package's Box artifact URL to `scripts/config/api-html-artifacts.json`. +3. If the package ships tutorials that live in `docs/tutorials/`, list their slugs in the `tutorials` array of the `Pkg` constructor call. +4. Run the pipeline once with a local artifact using `-a ` to verify output before uploading to Box. + ## Generate new API docs Use this process when we want to publish new API docs, such as when we release a new version of a package like Qiskit SDK. @@ -357,12 +394,17 @@ All release types start with the following steps: - Qiskit SDK: https://github.com/Qiskit/qiskit/actions/workflows/docs_deploy.yml - Runtime: https://github.com/Qiskit/qiskit-ibm-runtime/actions/workflows/docs.yml - Transpiler Service: https://github.com/Qiskit/qiskit-ibm-transpiler/actions/workflows/upload-docs.yml - - qiskit-addon-acq-tensor: https://github.com/Qiskit/qiskit-addon-aqc-tensor/actions/workflows/docs.yml + - qiskit-addon-aqc-tensor: https://github.com/Qiskit/qiskit-addon-aqc-tensor/actions/workflows/docs.yml - qiskit-addon-obp: https://github.com/Qiskit/qiskit-addon-obp/actions/workflows/docs.yml - qiskit-addon-mpf: https://github.com/Qiskit/qiskit-addon-mpf/actions/workflows/docs.yml - qiskit-addon-sqd: https://github.com/Qiskit/qiskit-addon-sqd/actions/workflows/docs.yml + - qiskit-addon-sqd-hpc: https://github.com/Qiskit/qiskit-addon-sqd-hpc/actions/workflows/docs.yml - qiskit-addon-cutting: https://github.com/Qiskit/qiskit-addon-cutting/actions/workflows/docs.yml - qiskit-addon-utils: https://github.com/Qiskit/qiskit-addon-utils/actions/workflows/docs.yml + - qiskit-addon-pna: https://github.com/Qiskit/qiskit-addon-pna/actions/workflows/docs.yml + - qiskit-addon-slc: https://github.com/Qiskit/qiskit-addon-slc/actions/workflows/docs.yml + - qiskit-addon-opt-mapper: https://github.com/Qiskit/qiskit-addon-opt-mapper/actions/workflows/docs.yml + - pauli-prop: https://github.com/Qiskit/pauli-prop/actions/workflows/docs.yml 2. Find the run for the release by looking at the middle column with the blue text; look for the version number, like `0.45.2`. For the `rc1` release, look for `main` in blue text and a run description like "Prepare 2.3.0rc1". 3. Click the CI run name. (Not the middle column with the blue link!) 4. In the left navbar, it should show as selected the "Summary" page with the house. diff --git a/docs/addons/pauli-prop/_toc.json b/docs/addons/pauli-prop/_toc.json new file mode 100644 index 00000000000..975e23d27a9 --- /dev/null +++ b/docs/addons/pauli-prop/_toc.json @@ -0,0 +1,45 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Pauli propagation 0.2", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/pauli-prop" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/pauli-prop/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "How to do something", + "url": "/docs/addons/pauli-prop/how_tos/do-something" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/pauli-prop" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Pauli propagation API reference", + "url": "/docs/api/pauli-prop" + } + ] + } + ] +} diff --git a/docs/addons/pauli-prop/how_tos/do-something.ipynb b/docs/addons/pauli-prop/how_tos/do-something.ipynb new file mode 100644 index 00000000000..72d99fd4fac --- /dev/null +++ b/docs/addons/pauli-prop/how_tos/do-something.ipynb @@ -0,0 +1,44 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to do something\"\n", + "description: \"How to do something for the latest version of Pauli propagation\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "0b915b9c-2673-4e05-9891-fcd72795cf8d", + "metadata": {}, + "source": [ + "# How to do something" + ] + } + ], + "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/pauli-prop/how_tos/index.mdx b/docs/addons/pauli-prop/how_tos/index.mdx new file mode 100644 index 00000000000..0e511186c39 --- /dev/null +++ b/docs/addons/pauli-prop/how_tos/index.mdx @@ -0,0 +1,13 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Pauli propagation" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +[![](/docs/images/addons/pauli-prop/nbsphinx-no-thumbnail.svg)](do-something) + +[How to do something](do-something) + diff --git a/docs/addons/pauli-prop/index.mdx b/docs/addons/pauli-prop/index.mdx new file mode 100644 index 00000000000..bea5e23dba0 --- /dev/null +++ b/docs/addons/pauli-prop/index.mdx @@ -0,0 +1,91 @@ +--- +title: "Pauli Prop" +description: "Documentation for the latest version of Pauli propagation" +--- + +# Pauli Prop + +## About + +Pauli propagation is a framework for approximating the evolution of operators in the Pauli basis under the action of other operators, such as quantum circuit gates and noise channels \[1] - \[5]. This approach can be effective when the operators involved are expected to remain sparse in the Pauli basis. The technique has been used to classically estimate expectation values of quantum systems and also to reduce the depths of quantum circuits to be run on a quantum processor \[6]. To learn how to use this package to simulate expectation values of quantum systems, check out the tutorial. + +This package provides a Rust-accelerated Python interface for performing Pauli propagation. The subroutines in this package may be used to implement: + +* Lightcone shading \[7] and other novel error mitigation techniques +* Operator backpropagation (OBP) \[6] +* Classical simulation of expectation values \[[tutorials](https://qiskit.github.io/pauli-prop/tutorials/index.html)] + +### Technical details + +* Rust-accelerated Python interface +* Support for noisy simulations \[[tutorial 3](https://qiskit.github.io/pauli-prop/tutorials/03_simulate_noisy_expectation_values.html)] +* Ability to truncate terms during evolution based on an absolute coefficient tolerance, a fixed number of terms in the evolving operator, or a combination of both. +* Ability to perform Pauli propagation in both the Schrödinger and Heisenberg frameworks. +* Novel technique for approximating the conjugation of a Pauli-sum operator by another Pauli operator. This heuristic implementation greedily generates contributions to the product expected to be most significant. +* Single-threaded + +### Computational requirements + +Both the memory and time cost for Pauli propagation routines generally scale with the size to which the evolved operator is allowed to grow. + +`propagate_through_rotation_gates`: As the Pauli operator is propagated in the Pauli basis under the action of a sequence of $N$ Pauli rotation gates of an $M$-qubit circuit, the number of terms will grow as $\mathcal{O}(2^N)$ towards a maximum of $4^M$ unique Pauli components. To control memory usage, the operator is truncated after application of each gate, which introduces some error proportional to the magnitudes of the truncated terms’ coefficients. The memory requirements are linear in the size of the evolved operator and runtime scales linearly in both the operator size and the number of gates. + +`propagate_through_operator`: Conjugates one operator in the Pauli basis by another by greedily accumulating terms in the sum, $\sum_{i,j,k}G^{\dagger}_iO_jG_k$, where $i,j,k$ are sparse indices over the Pauli basis. This implementation sorts the coefficients in each operator by descending magnitude then searches the 3D index space for the terms with the largest coefficients, starting with the origin $(0,0,0)$, and accumulating $(i,j,k)$ triplets up to a specified cutoff. The time spent searching can often be made negligible by increasing the search step size in $(i,j,k)$ space, which provides a cubic speedup for this subroutine. In our profiling, significant time can be spent sorting the operators and performing Pauli multiplication to generate the terms in the new operator. + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install pauli-prop +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Citing this project + +If you use this package in your research, please cite it according to `CITATON.bib` file included in this repository: + +```bibtex +@software{pauli-prop, + author = {Qiskit addons team}, + title = {{Pauli prop}}, + howpublished = {\url{https://github.com/Qiskit/pauli-prop}}, + year = {2025} +} +``` + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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. + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/pauli-prop). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/pauli-prop/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/pauli-prop/issues/new/choose) for tracking requests and bugs. + +## License + +[Apache License 2.0](https://github.com/Qiskit/pauli-prop/blob/main/LICENSE.txt) + + + +## References + +\[1] Tomislav Begušić, Johnnie Gray, Garnet Kin-Lic Chan, [Fast and converged classical simulations of evidence for the utility of quantum computing before fault tolerance](https://arxiv.org/abs/2308.05077), arXiv:2308.05077 \[quant-ph]. + +\[2] Nicolas Loizeau, et al., [Quantum many-body simulations with PauliStrings.jl](https://arxiv.org/abs/2410.09654), arXiv:2410.09654 \[quant-ph]. + +\[3] Manuel S. Rudolph, et al., [Pauli Propagation: A Computational Framework for Simulating Quantum Systems](https://arxiv.org/abs/2505.21606), arXiv:2505.21606 \[quant-ph]. + +\[4] Hrant Gharibyan, et al., [A Practical Guide to using Pauli Path Simulators for Utility-Scale Quantum Experiments](https://arxiv.org/abs/2507.10771), arXiv:2507.10771 \[quant-ph]. + +\[5] Lukas Broers, et al., [Scalable Simulation of Quantum Many-Body Dynamics with Or-Represented Quantum Algebra](https://arxiv.org/abs/2506.13241), arXiv:2506.13241 \[quant-ph]. + +\[6] Bryce Fuller, et al., [Improved Quantum Computation using Operator Backpropagation](https://arxiv.org/abs/2502.01897), arXiv:2502.01897 \[quant-ph]. + +\[7] Andrew Eddins, Minh C. Tran, Patrick Rall, [Lightcone shading for classically accelerated quantum error mitigation](https://arxiv.org/abs/2409.04401), arXiv:2409.04401 \[quant-ph]. + diff --git a/docs/addons/pauli-prop/install.mdx b/docs/addons/pauli-prop/install.mdx new file mode 100644 index 00000000000..28b662f11f5 --- /dev/null +++ b/docs/addons/pauli-prop/install.mdx @@ -0,0 +1,64 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Pauli propagation" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is ensure your Python environment is set up correctly. To create a new environment: + +## Pre-Installation + +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 +``` + +There are two primary ways to install this package – from PyPI or source. The preferred method is to install from PyPI: + +## Install from PyPI + +```sh +pip install pauli-prop +``` + +## Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `pauli-prop` repository. + +```sh +git clone git@github.com:Qiskit/pauli-prop.git +``` + +Next, install the Rust toolchain, upgrade pip, and enter the repository. Refer to the [Rust documentation](https://www.rust-lang.org/tools/install) for instructions on installing the toolchain. + +```sh +### ### +pip install --upgrade pip +cd pauli-prop +``` + +The next step is to install `pauli-prop` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab' +``` + diff --git a/docs/addons/qiskit-addon-aqc-tensor/_toc.json b/docs/addons/qiskit-addon-aqc-tensor/_toc.json new file mode 100644 index 00000000000..d33c34a9e9a --- /dev/null +++ b/docs/addons/qiskit-addon-aqc-tensor/_toc.json @@ -0,0 +1,59 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Approximate quantum compilation (AQC-Tensor) 0.3", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-aqc-tensor" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-aqc-tensor/install" + }, + { + "title": "Explanatory Material", + "url": "/docs/addons/qiskit-addon-aqc-tensor/explanation/index" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "How to use quimb.tensor.TNOptimizer directly", + "url": "/docs/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-aqc-tensor" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "collapsible": false, + "children": [ + { + "title": "Approximate quantum compilation for time evolution circuits", + "url": "/docs/tutorials/approximate-quantum-compilation-for-time-evolution" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Approximate quantum compilation (AQC-Tensor) API reference", + "url": "/docs/api/qiskit-addon-aqc-tensor" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-aqc-tensor/explanation/index.mdx b/docs/addons/qiskit-addon-aqc-tensor/explanation/index.mdx new file mode 100644 index 00000000000..2679f07975b --- /dev/null +++ b/docs/addons/qiskit-addon-aqc-tensor/explanation/index.mdx @@ -0,0 +1,86 @@ +--- +title: "Explanatory Material on AQC-Tensor" +description: "Explanatory Material on AQC-Tensor for the latest version of Approximate quantum compilation (AQC-Tensor)" +--- + + + +# Explanatory Material on AQC-Tensor + +## Background + +This Qiskit addon allows one to compress the *initial portion* of a circuit using Approximate Quantum Compilation (AQC) with tensor networks, following the method introduced by Ref. [\[1\]](#id4). + +In the following, we will assume that the input circuit has already been truncated such that its entire depth can be simulated by a tensor network with a reasonable bond dimension. In order for AQC to be useful on quantum hardware, one must use the resulting circuit as a state preparation procedure and then perform subsequent quantum computation on that state in a way that is beyond the reach of a tensor network simulator alone. + +In essence, AQC-Tensor requires three things as input: + +1. A description of the **target state** in the form of a tensor network. This may be generated by simulating a circuit on a tensor network simulator (this is what Ref. [\[1\]](#id4) did), or it could be generated in some other way, e.g., by performing time-dependent variational principle (TDVP) time evolution directly on a matrix-product state. +2. A parametrized **ansatz circuit**, ideally with hardware-efficient connectivity, such that it will have a reasonable depth on the target hardware. +3. **Initial parameters** to plug into the ansatz circuit, such that the resulting state is already a *good* approximation to the target state. + +The technique is to then use an iterative optimization method to make the *good* state as close to the target state as possible. + +\[The third item is not required *in principle*, but it really helps to give the optimizer a sensible starting point; otherwise, it is much more likely to get stuck in a local (but not global) minimum.] + +## Ansatz generation (motivation) + +As mentioned in the previous section, three closely related things are needed as input to AQC-Tensor. Rather than code all three individually, this package provides a [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit") function that can generate (2) and (3) from a circuit. In the case of Trotter evolution (and likely other circuit classes of interest), one can use the same circuit-generation function to generate (1) as well as the input to [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"), thus enabling the generation of all three inputs from a single function. The tutorial is a good demonstration of this re-use of a single circuit-generation function. + +Specifically, [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit") will take an input circuit and generate the ansatz circuit (2) and initial parameters (3) that follow the two-qubit connectivity of the input circuit. However, the returned circuit will be parametrized, based on generalized one- and two-qubit rotations. The initial parameters returned by the function, when plugged into the ansatz, will result in a circuit that is exactly equivalent to the circuit passed as input, up to a global phase. + +Given the ansatz generation function, the user must provide only two things in order to use AQC-Tensor: + +1. The **target state**, i.e., a *great* description of the desired state; and, +2. A **circuit** with the desired connectivity of the ansatz that prepares a *good* approximation to the target state. + +The optimization procedure then takes the *good* ansatz parameters and brings them as close as possible to the *great* description (the target state). + +In the case of a Trotter circuit, the same function can actually provide both things. For instance, the `spinhalf_trotter_circuit` function could be called with `num_trotter_steps=50` to produce the target state, then again with `num_trotter_steps=5` to produce the ansatz structure to be passed to `generate_ansatz_from_circuit`. (Note that the `evolution_time` is fixed, such that the time step `dt = evolution_time / num_trotter_steps`. the time per single Trotter step as `dt = evolution_time / num_trotter_steps`, so these two function calls would leave the total `evolution_time` fixed, while changing the time per Trotter step, `dt`.) So, in order for a user to apply AQC-Tensor to a new physical model of Trotter circuit, they would only need to generalize a single function with the details of that model. + +The ansatz used by `generate_ansatz_from_circuit` uses 9 parameters per two-qubit block, which is the theoretical minimum (see, e.g., Fig. 2 of [https://arxiv.org/abs/quant-ph/0308033](https://arxiv.org/abs/quant-ph/0308033)). The automatic ansatz is based on the [KAK decomposition](https://qiskit-extensions.github.io/circuit-knitting-toolbox/circuit_cutting/explanation/index.html#more-general-cut-two-qubit-gates-via-the-kak-decomposition), which parametrizes any two-qubit gate in terms of three parameters, up to single-qubit rotations. The single-qubit rotation are then decomposed as ZXZ, each of which has three parameters. So, each two-qubit block of the original circuit will result in 3 parameters for the two-qubit rotation, plus 3 parameters for an outgoing single-qubit rotation on each of the two qubits, for a total of 9 parameters per block. The ansatz is completed by adding a layer of single-qubit rotations to each active qubit at the start of the circuit. + +## Tensor-network simulation + +The simplest tensor network is a matrix-product state (MPS). + +Currently, AQC-Tensor supports the following tensor-network simulators: + +* Qiskit Aer’s MPS simulator +* Quimb’s [eager](https://quimb.readthedocs.io/en/latest/tensor/tensor-circuit-mps.html) [`CircuitMPS`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.CircuitMPS) simulator +* Quimb’s [lazy](https://quimb.readthedocs.io/en/latest/tensor/tensor-circuit.html) [`Circuit`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit) simulator (may work only on small circuits so far; we’re working to fix this soon with more clever contractions) + +The most important parameter of a tensor network is its maximum bond dimension, which limits how much entanglement it can represent (and thus to what depth a given circuit can be faithfully simulated). The bond dimension is often represented by the Greek letter $\chi$. + +Given a general circuit on $L$ qubits, a matrix-product state needs at most a bond dimension of $\chi_\mathrm{exact} = 2^{\lfloor L/2 \rfloor}$ in order to be able to simulate it to *any* depth. Of course, general circuits on 100+ qubits cannot be classically simulated, so it will be intractable to set the bond dimension this high for those circuits. + +For this reason, if you are attempting to experiment with AQC-Tensor on a toy problem with few qubits, it is important to ensure that $\chi < 2^{\lfloor L/2 \rfloor}$. Otherwise, any circuit can be simulated to any depth, and there is no point in performing AQC. + +### Objective function + +Currently, this addon provides one very simple objective, [`MaximizeStateFidelity`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity"), whose [`loss_function()`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function") is equivalent to Eq. (7) in Ref. [\[1\]](#id4). When called with an array of parameters, the [`loss_function()`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function") method returns the value and gradient of the objective function as a 2-tuple. + +### Gradient + +This package provides a few different methods for calculating the gradient. + +The Aer backend always uses the explicit gradient code in the `explicit_gradient` module. + +The Quimb backend will typically be used with an automatic differentiation backend; the user is to select a backend from among those supported by Quimb. Alternatively, one can instead pass `"explicit"` as the `autodiff_backend` when instantiating the [`QuimbSimulator`](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator#qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator "qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator") object; in this case, the `explicit_gradient` module will be used. It is only recommended to use explicit gradients with Quimb’s eager [`CircuitMPS`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.CircuitMPS) simulator, not the lazy [`Circuit`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit) simulator. + +Regardless of which backend is chosen, the gradient code can understand linear parameter expressions (ParameterExpression objects). This support is essential, as linear expressions are returned by the ansatz generation code. + +### Optimization method + +Users are encouraged to use [`scipy.optimize`](https://docs.scipy.org/doc/scipy/reference/optimize.html#module-scipy.optimize "(in SciPy v1.17.0)") to perform the optimization. + +L-BFGS is the optimizer demonstrated in the tutorial notebook. It works well in practice because it uses the function value and its gradient to approximate the Hessian. It works well when given an initial point and seems to work particularly well in the case of Trotter circuits. However, it might early terminate if it starts in a barren plateau. In that case, performing a handful of steps using the ADAM optimizer first may help. + +## References + + + +\[1] ([1](#id1),[2](#id2),[3](#id3)) + +[arXiv:2301.08609v6](https://arxiv.org/abs/2301.08609v6). + diff --git a/docs/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer.ipynb b/docs/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer.ipynb new file mode 100644 index 00000000000..0fb66ea00a2 --- /dev/null +++ b/docs/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer.ipynb @@ -0,0 +1,610 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to use quimb.tensor.TNOptimizer directly\"\n", + "description: \"How to use quimb.tensor.TNOptimizer directly for the latest version of Approximate quantum compilation (AQC-Tensor)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "d0791829-7938-47b6-8f01-068e981f60c5", + "metadata": {}, + "source": [ + "# How to use `quimb.tensor.TNOptimizer` directly\n", + "\n", + "Quimb provides [a mechanism for optimizing tensor networks](https://quimb.readthedocs.io/en/latest/tensor-optimization.html) through its `TNOptimizer` interface. The Quimb backend provided by this addon uses this under the hood. This how-to guide demonstrates how to work with this object directly, in case some users want more direct access to it.\n", + "\n", + "### Set up a model Hamiltonian" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8fdf1603-b541-46e7-871d-bf6b0ea8afce", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:33:55.273937Z", + "iopub.status.busy": "2026-05-14T20:33:55.273667Z", + "iopub.status.idle": "2026-05-14T20:33:55.740747Z", + "shell.execute_reply": "2026-05-14T20:33:55.739828Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_3158/2599302630.py:1: DeprecationWarning: Using Qiskit with Python 3.9 is deprecated as of the 2.1.0 release. Support for running Qiskit with Python 3.9 will be removed in the 2.3.0 release, which coincides with when Python 3.9 goes end of life.\n", + " from qiskit.transpiler import CouplingMap\n" + ] + } + ], + "source": [ + "from qiskit.transpiler import CouplingMap\n", + "from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian\n", + "\n", + "# Generate some coupling map to use for this example\n", + "coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)\n", + "\n", + "# Choose a 10-qubit circle on this coupling map\n", + "reduced_coupling_map = coupling_map.reduce([0, 13, 1, 14, 10, 16, 4, 15, 3, 9])\n", + "\n", + "# Get a qubit operator describing the Ising field model\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " reduced_coupling_map,\n", + " coupling_constants=(0.0, 0.0, 1.0),\n", + " ext_magnetic_field=(0.4, 0.0, 0.0),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3f46356f-e975-4705-8788-87566e2c4905", + "metadata": {}, + "source": [ + "### Set up quimb simulator with default options" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fc38c412-f55d-4453-8c38-b84286f43ee8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:33:55.744560Z", + "iopub.status.busy": "2026-05-14T20:33:55.743981Z", + "iopub.status.idle": "2026-05-14T20:34:14.421419Z", + "shell.execute_reply": "2026-05-14T20:34:14.420606Z" + } + }, + "outputs": [], + "source": [ + "import quimb.tensor as qtn\n", + "\n", + "from qiskit_addon_aqc_tensor.simulation.quimb import (\n", + " QuimbSimulator,\n", + " qiskit_ansatz_to_quimb,\n", + " recover_parameters_from_quimb,\n", + ")\n", + "\n", + "simulator = QuimbSimulator(qtn.Circuit)" + ] + }, + { + "cell_type": "markdown", + "id": "b145bc16-7271-4647-8661-9f88023a790c", + "metadata": {}, + "source": [ + "### Generate target circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "072bfaff-5a7f-4639-899a-a5a78e6af3ba", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:34:14.424484Z", + "iopub.status.busy": "2026-05-14T20:34:14.424167Z", + "iopub.status.idle": "2026-05-14T20:34:19.262641Z", + "shell.execute_reply": "2026-05-14T20:34:19.261802Z" + } + }, + "outputs": [], + "source": [ + "from qiskit.synthesis import SuzukiTrotter\n", + "from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit\n", + "\n", + "from qiskit_addon_aqc_tensor.simulation import (\n", + " compute_overlap,\n", + " tensornetwork_from_circuit,\n", + ")\n", + "\n", + "evolution_time = 0.4\n", + "\n", + "target_circuit = generate_time_evolution_circuit(\n", + " hamiltonian,\n", + " synthesis=SuzukiTrotter(reps=8),\n", + " time=evolution_time,\n", + ")\n", + "\n", + "target_tns = tensornetwork_from_circuit(target_circuit, simulator)" + ] + }, + { + "cell_type": "markdown", + "id": "4b2735b7-0350-41e6-af80-6c9cda2083b7", + "metadata": {}, + "source": [ + "### Generate ansatz from a shallower circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5d07640a-d7ee-4966-80e8-dd084ffb0da3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:34:19.265858Z", + "iopub.status.busy": "2026-05-14T20:34:19.265645Z", + "iopub.status.idle": "2026-05-14T20:34:22.261196Z", + "shell.execute_reply": "2026-05-14T20:34:22.260255Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit_addon_aqc_tensor.ansatz_generation import (\n", + " AnsatzBlock,\n", + " generate_ansatz_from_circuit,\n", + ")\n", + "\n", + "initial_shallow_circuit = generate_time_evolution_circuit(\n", + " hamiltonian,\n", + " synthesis=SuzukiTrotter(reps=2),\n", + " time=evolution_time,\n", + ")\n", + "ansatz, initial_parameters = generate_ansatz_from_circuit(initial_shallow_circuit)\n", + "ansatz = ansatz.decompose(AnsatzBlock)\n", + "ansatz.draw(\"mpl\", fold=-1)" + ] + }, + { + "cell_type": "markdown", + "id": "172012af-5499-4bdf-82b1-c5ba2d0350fe", + "metadata": {}, + "source": [ + "### Initialize objective function" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8b039335-3e00-409d-ac75-983cfbe88c3a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:34:22.264966Z", + "iopub.status.busy": "2026-05-14T20:34:22.264686Z", + "iopub.status.idle": "2026-05-14T20:34:22.267983Z", + "shell.execute_reply": "2026-05-14T20:34:22.267271Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity\n", + "\n", + "objective = MaximizeStateFidelity(target_tns, None, None)" + ] + }, + { + "cell_type": "markdown", + "id": "4ba05493-be0b-4d91-8ca7-1065f8b0bde8", + "metadata": {}, + "source": [ + "### Convert Qiskit ansatz and initial parameters to a Quimb parametrized circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2d60342e-393d-4604-86af-f20e0674c982", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:34:22.269874Z", + "iopub.status.busy": "2026-05-14T20:34:22.269692Z", + "iopub.status.idle": "2026-05-14T20:34:22.381632Z", + "shell.execute_reply": "2026-05-14T20:34:22.380737Z" + } + }, + "outputs": [], + "source": [ + "circ, conversion_context = qiskit_ansatz_to_quimb(ansatz, initial_parameters)" + ] + }, + { + "cell_type": "markdown", + "id": "a05591a0-5877-49e8-82aa-5fe06c11b718", + "metadata": {}, + "source": [ + "### Perform optimization of Quimb circuit using automatic differentiation" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7f3eaa20-3332-4fd1-811c-48a7a3078242", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-14T20:34:22.384207Z", + "iopub.status.busy": "2026-05-14T20:34:22.383991Z", + "iopub.status.idle": "2026-05-14T20:34:55.115108Z", + "shell.execute_reply": "2026-05-14T20:34:55.114333Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + " 0%| | 0/20 [00:00 + +# Installation Instructions + +Let’s see how to install AQC-Tensor. The first thing to do is choose how you’re going to run and install the packages. There are two primary ways to do this: + +* [Option 1: Pip Installation](#option-1) +* [Option 2: Install from Source](#option-2) + +## Pre-Installation + +Users who wish to install locally (using either [Option 1: Pip Installation](#option-1) or [Option 2: Install from Source](#option-2)) are encouraged to follow a brief set of common instructions to prepare a Python environment for installation of AQC-Tensor: + +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: Pip Installation + +Upgrade pip and install the AQC-Tensor package. To meaningfully use the package, you must also install at least one tensor network backend. The below snippet installs the addon, along with quimb (for tensor network support) and jax (for automatic differentiation). + +```sh +pip install --upgrade pip +pip install 'qiskit-addon-aqc-tensor[quimb-jax]' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the tutorials locally may want to install from source. + +In either case, the first step is to clone the AQC-Tensor repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-aqc-tensor.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-aqc-tensor +``` + +The next step is to install AQC-Tensor to the virtual environment. If you plan on running the tutorials, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may want to install the `dev` dependencies. + +Adjust the options below to suit your needs. + +```sh +pip install tox jupyterlab -e '.[notebook-dependencies,dev]' +``` + +If you installed the notebook dependencies, you can get started with AQC-Tensor by running the notebooks in the docs. + +```python +cd docs/ +jupyter lab +``` + + + +## Platform Support + +We expect this package to work on [any Tier 1 platform supported by Qiskit](/docs/guides/install-qiskit#operating-system-support). + diff --git a/docs/addons/qiskit-addon-cutting/_toc.json b/docs/addons/qiskit-addon-cutting/_toc.json new file mode 100644 index 00000000000..4813b3cf117 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/_toc.json @@ -0,0 +1,156 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Circuit cutting 0.10", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-cutting" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-cutting/install" + }, + { + "title": "Explanatory Material", + "url": "/docs/addons/qiskit-addon-cutting/explanation/index" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "How to generate exact quasiprobability distributions from Sampler", + "url": "/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-quasi-dists-from-sampler" + }, + { + "title": "How to generate exact sampling coefficients", + "url": "/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients" + }, + { + "title": "How to perform a cutting workflow with multiple observables", + "url": "/docs/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables" + }, + { + "title": "How to place wire cuts using a single-qubit CutWire instruction", + "url": "/docs/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires" + } + ] + }, + { + "title": "API References", + "children": [ + { + "title": "Circuit cutting (qiskit_addon_cutting)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/qiskit-addon-cutting" + }, + { + "title": "Instructions (qiskit_addon_cutting.instructions)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/instructions" + }, + { + "title": "CutWire", + "url": "/docs/addons/qiskit-addon-cutting/stubs/instructions-cut-wire" + }, + { + "title": "Move", + "url": "/docs/addons/qiskit-addon-cutting/stubs/instructions-move" + }, + { + "title": "Quasi-Probability Decomposition (QPD) (qiskit_addon_cutting.qpd)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/qpd" + }, + { + "title": "QPDBasis", + "url": "/docs/addons/qiskit-addon-cutting/stubs/qpd-qpd-basis" + }, + { + "title": "BaseQPDGate", + "url": "/docs/addons/qiskit-addon-cutting/stubs/qpd-base-qpd-gate" + }, + { + "title": "SingleQubitQPDGate", + "url": "/docs/addons/qiskit-addon-cutting/stubs/qpd-single-qubit-qpd-gate" + }, + { + "title": "TwoQubitQPDGate", + "url": "/docs/addons/qiskit-addon-cutting/stubs/qpd-two-qubit-qpd-gate" + }, + { + "title": "Bitwise utilities (qiskit_addon_cutting.utils.bitwise)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-bitwise" + }, + { + "title": "Iteration utilities (qiskit_addon_cutting.utils.iteration)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-iteration" + }, + { + "title": "Observable grouping (qiskit_addon_cutting.utils.observable_grouping)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-observable-grouping" + }, + { + "title": "Observable terms (qiskit_addon_cutting.utils.observable_terms)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-observable-terms" + }, + { + "title": "Simulation utilities (qiskit_addon_cutting.utils.simulation)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-simulation" + }, + { + "title": "Transforms (qiskit_addon_cutting.utils.transforms)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-transforms" + }, + { + "title": "Transpiler passes (qiskit_addon_cutting.utils.transpiler_passes)", + "url": "/docs/addons/qiskit-addon-cutting/apidocs/utils-transpiler-passes" + }, + { + "title": "RemoveFinalReset", + "url": "/docs/addons/qiskit-addon-cutting/stubs/utils-transpiler-passes-remove-final-reset" + }, + { + "title": "ConsolidateResets", + "url": "/docs/addons/qiskit-addon-cutting/stubs/utils-transpiler-passes-consolidate-resets" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-cutting" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "collapsible": false, + "children": [ + { + "title": "Circuit cutting for depth reduction", + "url": "/docs/tutorials/depth-reduction-with-circuit-cutting" + }, + { + "title": "Circuit cutting for periodic boundary conditions", + "url": "/docs/tutorials/periodic-boundary-conditions-with-circuit-cutting" + }, + { + "title": "Wire cutting for expectation values estimation", + "url": "/docs/tutorials/wire-cutting" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Circuit cutting API reference", + "url": "/docs/api/qiskit-addon-cutting" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-cutting/explanation/index.mdx b/docs/addons/qiskit-addon-cutting/explanation/index.mdx new file mode 100644 index 00000000000..e7914f0c6a1 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/explanation/index.mdx @@ -0,0 +1,146 @@ +--- +title: "Explanatory material" +description: "Explanatory material for the latest version of Circuit cutting" +--- + + + +# Explanatory material + +## Overview of circuit cutting + +Circuit cutting is a technique to increase the size of circuits we can run on quantum hardware at the cost of an additional sampling overhead. A larger quantum circuit can be decomposed by cutting its gates and/or wires, resulting in smaller circuits which can be executed within the constraints of available quantum hardware. The results of these smaller circuits are combined to reconstruct the outcome of the original problem. Circuit cutting can also be used to engineer gates between distant qubits which would otherwise require a large swap overhead. + +## Key terms + +* subcircuits: The set of circuits resulting from cutting gates in a [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) and then separating the disconnected qubit subsets into smaller circuits. These circuits contain [`SingleQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-single-qubit-qpd-gate#qiskit_addon_cutting.qpd.SingleQubitQPDGate "qiskit_addon_cutting.qpd.SingleQubitQPDGate")s and will be used to instantiate each unique subexperiment. +* subexperiments: A term used to describe the unique circuit samples associated with a subcircuit. These circuits have had their [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate#qiskit_addon_cutting.qpd.BaseQPDGate "qiskit_addon_cutting.qpd.BaseQPDGate")s decomposed into local Qiskit gates and measurements. Subexperiments are the circuits sent to the backend for execution. +* decompose: We try to honor the Qiskit notion of “decompose” in the documentation and API, which loosely means transforming a gate into a less-abstracted representation. *Occasionally*, we may use the term “decompose” to refer to the act of inserting [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate#qiskit_addon_cutting.qpd.BaseQPDGate "qiskit_addon_cutting.qpd.BaseQPDGate") instances into quantum circuits as “decomposing” a gate or wire; however, we try to use terms like “partition” and “cut” when referring to this to avoid ambiguity with Qiskit language. + +## Circuit cutting as a quasiprobability decomposition (QPD) + +Quasiprobability decomposition is a technique which can be used to simulate quantum circuit executions that go beyond the actual capabilities of current quantum hardware while using that same hardware. It forms the basis of many error mitigation techniques, which allow simulating a noise-free quantum computer using a noisy one. Circuit cutting techniques, which allow simulating a quantum circuit using fewer qubits than would otherwise be necessary, can also be phrased in terms of a quasiprobability decomposition. No matter the goal, the cost of the quasiprobability decomposition is an exponential overhead in the number of circuit executions which must be performed. In certain cases, this tradeoff is worth it, because it can allow the estimation of quantities that would otherwise be impossible on today’s hardware. + +There are two types of cuts: gate cuts and wire cuts. Gate cuts, also known as “space-like” cuts, exist when the cut goes through a gate operating on two (or more) qubits. Wire cuts, also known as “time-like” cuts, are direct cuts through a qubit wire, essentially a single-qubit identity gate that has been cut into two pieces. In this package, a wire cut is represented by introducing a new qubit into the circuit and moving remaining operations after the cut identity gate to the new qubit; see [Wire cutting phrased as a two-qubit Move operation](#wire-cutting-as-move), below. + +There are [three settings](https://research.ibm.com/blog/circuit-knitting-with-classical-communication) to consider for circuit cutting. The first is where only local operations (LO) \[i.e., local *quantum* operations] are available. The other settings introduce classical communication between the circuit executions, which is known in the quantum information literature as LOCC, for [local operations and classical communication](https://en.wikipedia.org/wiki/LOCC). The LOCC can be either near-time, one-directional communication between the circuit executions (the second setting), or real-time, bi-directional communication (the third setting). + +As mentioned above, the cost of any simulation based on quasiprobability distribution is an exponential sampling overhead. The sampling overhead is the factor by which the overall number of shots must increase for the quasiprobability decomposition to result in the same amount of error, $\epsilon$, as one would get by executing the original circuit. The overhead of a cut gate depends on which gate is cut; see the final appendix of \[[1](https://arxiv.org/abs/2205.00016)] for details. For instance, a single cut CNOT gate incurs a sampling overhead of 9 \[[2](https://arxiv.org/abs/1909.07534),[6](https://arxiv.org/abs/2312.11638)]. A circuit with $n$ wire cuts incurs a sampling overhead of O($16^n$) when classical communication is not available (LO setting); this is reduced to O($4^n$) when classical communication is available (LOCC setting) \[[4](https://arxiv.org/abs/2302.03366)]. However, wire cutting with classical communication (LOCC) is not yet supported by this package (see issue [#264](https://github.com/Qiskit/qiskit-addon-cutting/issues/264)). + +The QPD can be given explicitly as follows: + +$$ +\mathcal{U} = \sum_i a_i \mathcal{F}_i , +$$ + +where $\mathcal{U}$ is the channel implementing the desired operation, and each $a_i$ is a real coefficient corresponding to a quantum channel, $\mathcal{F}_i$, that is realizable on hardware. + +Note that because this is a sum of channels, *not* a sum of unitaries, expectation values can be evaluated efficiently \[[2](https://arxiv.org/abs/1909.07534)]. + +Results equivalent to original unitary channel can be obtained by a post-processing method, given the coefficients $a_i$ and the outcome of each experiment corresponding to the different local ($\mathcal{F}_i$) channels. This post-processing boosts the magnitude of each measurement outcome by $\sum_i \left| a_i \right|$, resulting in a sampling overhead of that quantity squared \[[6](https://arxiv.org/abs/2312.11638)]: + +$$ +\mathrm{Sampling\ overhead} = \left( \sum_i \lvert a_i \rvert \right)^2 . +$$ + +For more detailed information on the quasiprobability decomposition technique, refer to the paper, Error mitigation for short-depth quantum circuits \[[5](https://arxiv.org/abs/1612.02058)]. + +The essential idea of gate cutting is to replace a two-qubit gate with a linear combination of quantum channels \[Eq. [(1)](#equation-eq-qpd)] that, when recombined, will allow reconstruction of the result for physically measurable quantities like expectation values. Note that “global phase” is not something that can be physically measured, so we can disregard it when specifying quasiprobability decompositions. + + + +## An example: cutting a [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate) + +As a basic and explicit example, let us consider the decomposition of a cut [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate). + +As shown in \[[2](https://arxiv.org/abs/1909.07534)], a quantum circuit which implements an [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate) can be simulated by performing six subexperiments where the [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate) in the original circuit has been replaced with only local (single-qubit) operations \[the $\mathcal{F}_i$‘s in Eq. [(1)](#equation-eq-qpd)]. The result is then reconstructed by combining the subexperiment results with certain coefficients \[the $a_i$‘s in Eq. [(1)](#equation-eq-qpd)], which can be either positive or negative. Given the $\theta$ parameter of the [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate), the six subexperiments are as follows: + +1. With coefficient $a_1 = \cos^2 (\theta/2)$, do nothing ($I \otimes I$, where $I$ is the identity operation on a single qubit). +2. With coefficient $a_2 = \sin^2 (\theta/2)$, perform a [`ZGate`](/docs/api/qiskit/qiskit.circuit.library.ZGate) on each qubit ($Z \otimes Z$). +3. With coefficient $a_3 = -\sin(\theta)/2$, perform a projective measurement in the Z basis on the first qubit and an [`SGate`](/docs/api/qiskit/qiskit.circuit.library.SGate) gate on the second qubit (denote this as $M_z \otimes S$). If the result of the measurement is 1, flip the sign of that outcome’s contribution during reconstruction. +4. With coefficient $a_4 = \sin(\theta)/2$, perform a projective measurement in the Z basis on the first qubit and an [`SdgGate`](/docs/api/qiskit/qiskit.circuit.library.SdgGate) gate on the second qubit (denote this as $M_z \otimes S^\dagger$). If the result of the measurement is 1, flip the sign of that outcome’s contribution during reconstruction. +5. Same as term 3 ($a_5 = a_3$), but swap the qubits ($S \otimes M_z$). +6. Same as term 4 ($a_6 = a_4$), but swap the qubits ($S^\dagger \otimes M_z$). + +Equation [(1)](#equation-eq-qpd) for [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate) can thus be written as a sum of the six terms listed above. The following plot shows the magnitude of each coeffient (negative coefficients are in orange) as a function of $\theta$. The square root of the optimal sampling overhead, denoted by $\gamma$, is given by the sum of the absolute coefficients. + +![../\_images/index-1.svg](/docs/images/addons/qiskit-addon-cutting/index-1.svg) + +Let’s consider some special points in this plot: + +* When $\theta = 0$, the gate has no effect, and the sampling overhead is 1. (Because the overhead is multiplicative, this is equivalent to there being no overhead.) +* When $\theta = \pi$, the gate is equivalent to $Z \otimes Z$ up to a global phase, and the sampling overhead is again 1. +* The maximum sampling overhead of $3^2 = 9$ is reached at a ZZ Rotation of $\theta=\pi/2$. We call this point the [`CXGate`](/docs/api/qiskit/qiskit.circuit.library.CXGate) family because this rotation is equivalent to a CXGate up to (single-qubit) local unitary operations. This point is also equivalent, up to local unitary operations, to [`CZGate`](/docs/api/qiskit/qiskit.circuit.library.CZGate), [`CYGate`](/docs/api/qiskit/qiskit.circuit.library.CYGate), [`CHGate`](/docs/api/qiskit/qiskit.circuit.library.CHGate), and [`ECRGate`](/docs/api/qiskit/qiskit.circuit.library.ECRGate). +* The ZZ rotation at $\theta=\pi/4$ has sampling overhead of $3+2\sqrt{2} \approx 5.828$. We call this the [`CSGate`](/docs/api/qiskit/qiskit.circuit.library.CSGate) family because this rotation is equivalent to a CSGate up to (single-qubit) local operations. This family also includes [`CSdgGate`](/docs/api/qiskit/qiskit.circuit.library.CSdgGate) and [`CSXGate`](/docs/api/qiskit/qiskit.circuit.library.CSXGate). +* Likewise, [`RXXGate`](/docs/api/qiskit/qiskit.circuit.library.RXXGate), [`RYYGate`](/docs/api/qiskit/qiskit.circuit.library.RYYGate), and [`RZXGate`](/docs/api/qiskit/qiskit.circuit.library.RZXGate) are all locally equivalent to [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate). The controlled rotations [`CRXGate`](/docs/api/qiskit/qiskit.circuit.library.CRXGate), [`CRYGate`](/docs/api/qiskit/qiskit.circuit.library.CRYGate), [`CRZGate`](/docs/api/qiskit/qiskit.circuit.library.CRZGate), and [`CPhaseGate`](/docs/api/qiskit/qiskit.circuit.library.CPhaseGate) at an angle of $2\theta$ are locally equivalent to [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate) at an angle of $\theta$. + +## More general cut two-qubit gates via the KAK decomposition + +We can formalize this notion of local unitary equivalence and expand it to all two-qubit gates using the KAK decomposition, given by + +$$ +U = (V_1 \otimes V_2) \exp \left[ i \left( \theta_x \, X \otimes X + \theta_y \, Y \otimes Y + \theta_z \, Z \otimes Z \right) \right] (V_3 \otimes V_4) , +$$ + +where $V_1$, $V_2$, $V_3$, and $V_4$ are local, single-qubit operations, and the two-qubit portion of the interaction is parametrized entirely by $\vec{\theta} = (\theta_x, \theta_y, \theta_z)$. By convention, we have chosen $\vec{\theta}$ to be in the “Weyl chamber” restricted by $\pi/4 \geq \theta_x \geq \theta_y \geq | \theta_z | \geq 0$ \[[6](https://arxiv.org/abs/2312.11638)]. For more information on the KAK decomposition, see Ref. \[[7](https://arxiv.org/abs/quant-ph/0209120)]. + +The code that generates subexperiments from the KAK decomposition currently follows Ref. \[[3](https://arxiv.org/abs/2006.11174)], which is now known to be non-optimal. A provably optimal method has been presented in Ref. \[[6](https://arxiv.org/abs/2312.11638)], but this newer method has not yet been implemented in this package (see issue [#531](https://github.com/Qiskit/qiskit-addon-cutting/issues/531)). + + + + + +## Wire cutting phrased as a two-qubit [`Move`](/docs/api/qiskit-addon-cutting/instructions-move#qiskit_addon_cutting.instructions.Move "qiskit_addon_cutting.instructions.Move") operation + +A wire cut is represented fundamentally by this package as a two-qubit [`Move`](/docs/api/qiskit-addon-cutting/instructions-move#qiskit_addon_cutting.instructions.Move "qiskit_addon_cutting.instructions.Move") instruction, which is defined as a reset of the second qubit followed by a swap of both qubits. Equivalently, the operation is defined as transferring the state of the first qubit wire to the second qubit wire, while simultaneously discarding the state of the second qubit wire (the first qubit ends up in state $\lvert 0 \rangle$). + +We have chosen to represent wire cuts in this way primarily because it is consistent with the way one must treat wire cuts when acting on physical qubits: for instance, a wire cut might take the state of physical qubit $n$ and continue it as physical qubit $m$ after the cut. Our choice also has the benefit of allowing us to think of “instruction cutting” as a unified framework for considering both wire cuts and gate cuts in the same formalism, being that a wire cut is just a cut [`Move`](/docs/api/qiskit-addon-cutting/instructions-move#qiskit_addon_cutting.instructions.Move "qiskit_addon_cutting.instructions.Move") instruction. + +More information on this formalism is given in Sec. 3 of Ref. \[[4](https://arxiv.org/abs/2302.03366)] + +If you prefer to place cut wires abstractly on a single qubit wire, please see the [how-to guide on placing wire cuts using a single-qubit instruction](how-tos/how-to-specify-cut-wires), which explains how to use the [`cut_wires()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#qiskit_addon_cutting.cut_wires "qiskit_addon_cutting.cut_wires") function to convert a circuit with [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire#qiskit_addon_cutting.instructions.CutWire "qiskit_addon_cutting.instructions.CutWire") instructions to a circuit with [`Move`](/docs/api/qiskit-addon-cutting/instructions-move#qiskit_addon_cutting.instructions.Move "qiskit_addon_cutting.instructions.Move")s on additional qubits. + +## Sample weights in the Qiskit addon for circuit cutting + +In this package, the number of samples taken from the distribution is generally controlled by a `num_samples` argument, and each sample has an associated weight which is used during expectation value reconstruction. Each weight with absolute value above a threshold of 1 / `num_samples` will be evaluated exactly. The remaining low-probability elements – those in the tail of the distribution – will then be sampled, resulting in at most `num_samples` unique weights. Setting `num_samples` to infinity indicates that all weights should be generated rigorously, rather than by sampling from the distribution. + +Much of the circuit cutting literature describes a process where we sample from the distribution, take a single shot, then sample from the distribution again and repeat; however, this is not feasible in practice, so we instead perform all sampling upfront. For now, because of limitations in version 1 of the Qiskit primitives, we take a fixed number of shots for each considered subexperiment and send the subexperiments to the backend(s) in batches. During reconstruction, each subexperiment contributes to the final result with proportion equal to its weight. One must ensure the number of shots taken is sufficient for the heaviest weighted subexperiment. In the future, we plan to support passing an individual `shots` count with each subexperiment to Qiskit Runtime, so that each subexperiment will be run with a number of shots proportional to that subexperiment’s weight in the final result (see issue [#532](https://github.com/Qiskit/qiskit-addon-cutting/issues/532)). This per-experiment shots count is a new feature enabled by version 2 of the Qiskit primitives. + +## Sampling overhead reference table + +The below table provides the sampling overhead factor for a variety of two-qubit instructions, provided that only a single instruction is cut. + +| Instruction(s) | KAK decomposition angles | Sampling overhead factor | | | | | | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | --------------------------- | --------- | -------------------- | -------------------- | ----------------- | -------------------- | -------------------------------------------------------------- | +| [`CSGate`](/docs/api/qiskit/qiskit.circuit.library.CSGate), [`CSdgGate`](/docs/api/qiskit/qiskit.circuit.library.CSdgGate), [`CSXGate`](/docs/api/qiskit/qiskit.circuit.library.CSXGate) | $(\pi/8, 0, 0)$ | $3+2\sqrt{2} \approx 5.828$ | | | | | | | +| [`CXGate`](/docs/api/qiskit/qiskit.circuit.library.CXGate), [`CYGate`](/docs/api/qiskit/qiskit.circuit.library.CYGate), [`CZGate`](/docs/api/qiskit/qiskit.circuit.library.CZGate), [`CHGate`](/docs/api/qiskit/qiskit.circuit.library.CHGate), [`ECRGate`](/docs/api/qiskit/qiskit.circuit.library.ECRGate) | $(\pi/4, 0, 0)$ | $3^2=9$ | | | | | | | +| [`iSwapGate`](/docs/api/qiskit/qiskit.circuit.library.iSwapGate), [`DCXGate`](/docs/api/qiskit/qiskit.circuit.library.DCXGate) | $(\pi/4, \pi/4, 0)$ | $7^2=49$ | | | | | | | +| [`SwapGate`](/docs/api/qiskit/qiskit.circuit.library.SwapGate) | $(\pi/4,\pi/4,\pi/4)$ | | | | | | | | +| [`RXXGate`](/docs/api/qiskit/qiskit.circuit.library.RXXGate), [`RYYGate`](/docs/api/qiskit/qiskit.circuit.library.RYYGate), [`RZZGate`](/docs/api/qiskit/qiskit.circuit.library.RZZGate), [`RZXGate`](/docs/api/qiskit/qiskit.circuit.library.RZXGate) | \$( | \theta/2 | , 0, 0)\$ | \$\left\[1 + 2 \left | \sin(\theta)\right | \right]^2\$ | | | +| [`CRXGate`](/docs/api/qiskit/qiskit.circuit.library.CRXGate), [`CRYGate`](/docs/api/qiskit/qiskit.circuit.library.CRYGate), [`CRZGate`](/docs/api/qiskit/qiskit.circuit.library.CRZGate), [`CPhaseGate`](/docs/api/qiskit/qiskit.circuit.library.CPhaseGate) | \$( | \theta/4 | , 0, 0)\$ | \$\left\[1 + 2 \left | \sin(\theta/2)\right | \right]^2\$ | | | +| [`XXPlusYYGate`](/docs/api/qiskit/qiskit.circuit.library.XXPlusYYGate), [`XXMinusYYGate`](/docs/api/qiskit/qiskit.circuit.library.XXMinusYYGate) | \$( | \theta/4 | , | \theta/4 | ,0)\$ | \$\left\[1+4\left | \sin(\theta/2)\right | +2\sin^2(\theta/2)\right]^2$(independent of$\beta\$ parameter) | +| [`Move`](/docs/api/qiskit-addon-cutting/instructions-move#qiskit_addon_cutting.instructions.Move "qiskit_addon_cutting.instructions.Move") (cut wire) without classical communication (i.e., in the LO setting) | not applicable | $4^2=16$ | | | | | | | + +## Current limitations + +* The workflow only allows taking the *expectation value* of observables with respect to a circuit. Limited support for reconstructing an output probability distribution may be added to a future version of this package (see issue [#259](https://github.com/Qiskit/qiskit-addon-cutting/issues/259)). +* Due to current code limitations, some of the generated subexperiments are redundant. This can result in more subexperiments than expected, particularly when using wire cutting. This is tracked by issue [#262](https://github.com/Qiskit/qiskit-addon-cutting/issues/262). + +## References + +This module is based on the theory described in the following papers: + +\[1] Christophe Piveteau, David Sutter, *Circuit knitting with classical communication*, [https://arxiv.org/abs/2205.00016](https://arxiv.org/abs/2205.00016) + +\[2] Kosuke Mitarai, Keisuke Fujii, *Constructing a virtual two-qubit gate by sampling single-qubit operations*, [https://arxiv.org/abs/1909.07534](https://arxiv.org/abs/1909.07534) + +\[3] Kosuke Mitarai, Keisuke Fujii, *Overhead for simulating a non-local channel with local channels by quasiprobability sampling*, [https://arxiv.org/abs/2006.11174](https://arxiv.org/abs/2006.11174) + +\[4] Lukas Brenner, Christophe Piveteau, David Sutter, *Optimal wire cutting with classical communication*, [https://arxiv.org/abs/2302.03366](https://arxiv.org/abs/2302.03366) + +\[5] K. Temme, S. Bravyi, and J. M. Gambetta, *Error mitigation for short-depth quantum circuits*, [https://arxiv.org/abs/1612.02058](https://arxiv.org/abs/1612.02058) + +\[6] Lukas Schmitt, Christophe Piveteau, David Sutter, *Cutting circuits with multiple two-qubit unitaries*, [https://arxiv.org/abs/2312.11638](https://arxiv.org/abs/2312.11638) + +\[7] Jun Zhang, Jiri Vala, K. Birgitta Whaley, Shankar Sastry, *A geometric theory of non-local two-qubit operations*, [https://arxiv.org/abs/quant-ph/0209120](https://arxiv.org/abs/quant-ph/0209120) + diff --git a/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-quasi-dists-from-sampler.ipynb b/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-quasi-dists-from-sampler.ipynb new file mode 100644 index 00000000000..6a41797a153 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-quasi-dists-from-sampler.ipynb @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f9e40036", + "metadata": {}, + "source": [ + "## How to generate exact quasiprobability distributions from Sampler\n", + "\n", + "This how-to guide is intended to show users how they can generate statevector-based quasiprobability distributions through the `qiskit.primitives.BaseSampler` interface." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "072055cb", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:50.653585Z", + "iopub.status.busy": "2025-03-28T16:50:50.653274Z", + "iopub.status.idle": "2025-03-28T16:50:51.088193Z", + "shell.execute_reply": "2025-03-28T16:50:51.087485Z" + } + }, + "outputs": [], + "source": [ + "from qiskit import QuantumCircuit\n", + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "from qiskit_addon_cutting import (\n", + " partition_problem,\n", + " generate_cutting_experiments,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "940334fd", + "metadata": {}, + "source": [ + "Prepare inputs to `generate_cutting_experiments`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dc4af922", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:51.091193Z", + "iopub.status.busy": "2025-03-28T16:50:51.090943Z", + "iopub.status.idle": "2025-03-28T16:50:51.116996Z", + "shell.execute_reply": "2025-03-28T16:50:51.116317Z" + } + }, + "outputs": [], + "source": [ + "circuit = QuantumCircuit(2)\n", + "circuit.h(0)\n", + "circuit.cx(0, 1)\n", + "observable = SparsePauliOp([\"ZZ\"])\n", + "partitioned_problem = partition_problem(\n", + " circuit=circuit, partition_labels=\"AB\", observables=observable.paulis\n", + ")\n", + "subcircuits = partitioned_problem.subcircuits\n", + "subobservables = partitioned_problem.subobservables" + ] + }, + { + "cell_type": "markdown", + "id": "d6361a9d", + "metadata": {}, + "source": [ + "Call `generate_cutting_experiments`" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d095701f", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:51.119755Z", + "iopub.status.busy": "2025-03-28T16:50:51.119556Z", + "iopub.status.idle": "2025-03-28T16:50:51.128723Z", + "shell.execute_reply": "2025-03-28T16:50:51.127940Z" + } + }, + "outputs": [], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=1000,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "bc59b1be", + "metadata": {}, + "source": [ + "In order to calculate exact quasiprobability distributions for circuits with mid-circuit measurements, users will need to use the `ExactSampler` class from `qiskit_addon_cutting.utils.simulation`. The Qiskit Samplers do not support mid-circuit measurements in statevector mode." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7a74f709", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:51.131476Z", + "iopub.status.busy": "2025-03-28T16:50:51.130959Z", + "iopub.status.idle": "2025-03-28T16:50:51.135282Z", + "shell.execute_reply": "2025-03-28T16:50:51.134551Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_cutting.utils.simulation import ExactSampler\n", + "\n", + "exact_sampler = ExactSampler()" + ] + }, + { + "cell_type": "markdown", + "id": "8d050fbe", + "metadata": {}, + "source": [ + "If `ExactSampler` is used, the quasiprobability distributions returned from `generate_cutting_experiments` will be exact and generated from the statevectors of the subexperiments." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7019d781", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:51.137659Z", + "iopub.status.busy": "2025-03-28T16:50:51.137468Z", + "iopub.status.idle": "2025-03-28T16:50:51.148731Z", + "shell.execute_reply": "2025-03-28T16:50:51.147960Z" + } + }, + "outputs": [], + "source": [ + "results = {\n", + " label: exact_sampler.run(subexperiment).result()\n", + " for label, subexperiment in subexperiments.items()\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients.ipynb b/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients.ipynb new file mode 100644 index 00000000000..22592b66f83 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients.ipynb @@ -0,0 +1,403 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ccbf48d3", + "metadata": {}, + "source": [ + "## How to generate exact sampling coefficients\n", + "\n", + "This how-to guide is intended to show users how they can generate exact sampling coefficients to be used in reconstructing the simulated expectation value of the original circuit.\n", + "\n", + "First, we set up a simple cutting problem following the [first tutorial](../tutorials/01_gate_cutting_to_reduce_circuit_width.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "dc54656b", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:52.873918Z", + "iopub.status.busy": "2025-03-28T16:50:52.873283Z", + "iopub.status.idle": "2025-03-28T16:50:53.390761Z", + "shell.execute_reply": "2025-03-28T16:50:53.390002Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.circuit.library import efficient_su2\n", + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "from qiskit_addon_cutting import (\n", + " partition_problem,\n", + " generate_cutting_experiments,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dd147239", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:53.393546Z", + "iopub.status.busy": "2025-03-28T16:50:53.393230Z", + "iopub.status.idle": "2025-03-28T16:50:54.346876Z", + "shell.execute_reply": "2025-03-28T16:50:54.346177Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "circuit = efficient_su2(4, entanglement=\"linear\", reps=2)\n", + "circuit.assign_parameters([0.8] * len(circuit.parameters), inplace=True)\n", + "observable = SparsePauliOp([\"ZZZZ\"])\n", + "circuit.draw(\"mpl\", scale=0.8)" + ] + }, + { + "cell_type": "markdown", + "id": "0bce456c", + "metadata": {}, + "source": [ + "Partition the circuit between qubits 1 and 2 by cutting 2 CNOT gates." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d4ccf5b8", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.349628Z", + "iopub.status.busy": "2025-03-28T16:50:54.349194Z", + "iopub.status.idle": "2025-03-28T16:50:54.584073Z", + "shell.execute_reply": "2025-03-28T16:50:54.583316Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "partitioned_problem = partition_problem(\n", + " circuit=circuit, partition_labels=\"AABB\", observables=observable.paulis\n", + ")\n", + "subcircuits = partitioned_problem.subcircuits\n", + "bases = partitioned_problem.bases\n", + "subobservables = partitioned_problem.subobservables\n", + "subcircuits[\"A\"].draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "44956cbb", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.586659Z", + "iopub.status.busy": "2025-03-28T16:50:54.586391Z", + "iopub.status.idle": "2025-03-28T16:50:54.811013Z", + "shell.execute_reply": "2025-03-28T16:50:54.810276Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subcircuits[\"B\"].draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "fce55187", + "metadata": {}, + "source": [ + "### Demonstrate how to obtain all weights exactly\n", + "\n", + "If you wish to calculate all weights exactly, no matter how small, you can achieve this by passing infinity (`np.inf`) to `num_samples`:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8c56282f", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.813264Z", + "iopub.status.busy": "2025-03-28T16:50:54.813049Z", + "iopub.status.idle": "2025-03-28T16:50:54.867519Z", + "shell.execute_reply": "2025-03-28T16:50:54.866771Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), ),\n", + " (np.float64(-0.24999999999999992), ),\n", + " (np.float64(0.24999999999999992), )]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=np.inf,\n", + ")\n", + "coefficients" + ] + }, + { + "cell_type": "markdown", + "id": "b5fc6579", + "metadata": {}, + "source": [ + "### Demonstrate how to find the minimum `num_samples` needed to retrieve all exact weights for 2 CNOT cuts\n", + "\n", + "When `num_samples` is set to a finite number, each weight whose absolute value is above a threshold of 1 / `num_samples` will be evaluated exactly. The remaining weights -- those in the tail of the distribution -- will then be sampled from, resulting in at most `num_samples` unique weights.\n", + "\n", + "In the case of a CNOT gate -- or any gate equivalent to it up to single-qubit unitaries -- each of the six weights of the quasi-probability decomposition have the same magnitude, so each gets sampled with a probability of $1/6$:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "78539fcc", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.870158Z", + "iopub.status.busy": "2025-03-28T16:50:54.869670Z", + "iopub.status.idle": "2025-03-28T16:50:54.874200Z", + "shell.execute_reply": "2025-03-28T16:50:54.873577Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mapping probabilities for a CNOT decomposition: [0.16666667 0.16666667 0.16666667 0.16666667 0.16666667 0.16666667]\n" + ] + } + ], + "source": [ + "print(f\"Mapping probabilities for a CNOT decomposition: {bases[0].probabilities}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5770cf75", + "metadata": {}, + "source": [ + "In this example, we have cut two CNOT gates. Given that the probability of any given mapping in a CNOT decomposition is $1/6$, the probability of any given mapping in the _joint_ distribution combining the two cut CNOT gates is $(1/6)^2$. Therefore, we need to take at least $6^2$ weights in order to retrieve all exact weights from `generate_cutting_experiments`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f07a6cc3", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.876590Z", + "iopub.status.busy": "2025-03-28T16:50:54.876398Z", + "iopub.status.idle": "2025-03-28T16:50:54.881758Z", + "shell.execute_reply": "2025-03-28T16:50:54.881069Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of samples needed to retrieve exact weights: 36.0\n" + ] + } + ], + "source": [ + "from qiskit_addon_cutting.qpd import QPDBasis\n", + "from qiskit.circuit.library.standard_gates import CXGate\n", + "\n", + "qpd_basis_cx = QPDBasis.from_instruction(CXGate())\n", + "\n", + "\n", + "def _min_nonzero(seq, /):\n", + " \"\"\"Return the minimum value in a sequence, ignoring values near zero.\"\"\"\n", + " return min(x for x in seq if not np.isclose(x, 0))\n", + "\n", + "\n", + "num_cx_cuts = 2\n", + "\n", + "print(\n", + " f\"Number of samples needed to retrieve exact weights: {1 / _min_nonzero(qpd_basis_cx.probabilities)**num_cx_cuts}\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "deb6ad1e", + "metadata": {}, + "source": [ + "#### Observe the coefficient weights returned from `generate_cutting_experiments` are `WeightType.EXACT`\n", + "\n", + "Above, we determined 36 samples would trigger the coefficients to be returned as exact. Here we set `num_samples` to exactly 36 to test this." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "43d32869", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:54.884011Z", + "iopub.status.busy": "2025-03-28T16:50:54.883576Z", + "iopub.status.idle": "2025-03-28T16:50:54.942752Z", + "shell.execute_reply": "2025-03-28T16:50:54.942054Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), ),\n", + " (np.float64(-0.25), ),\n", + " (np.float64(0.25), )]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=36,\n", + ")\n", + "coefficients" + ] + } + ], + "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-cutting/how-tos/how-to-provide-multiple-observables.ipynb b/docs/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables.ipynb new file mode 100644 index 00000000000..1f379dac2f4 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables.ipynb @@ -0,0 +1,393 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to perform a cutting workflow with multiple observables\"\n", + "description: \"How to perform a cutting workflow with multiple observables for the latest version of Circuit cutting\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a42230b5-41d9-465c-904a-7d431cc72595", + "metadata": {}, + "source": [ + "# How to perform a cutting workflow with multiple observables" + ] + }, + { + "cell_type": "markdown", + "id": "6902602f-1392-4321-9cbe-5eb0921909b0", + "metadata": {}, + "source": [ + "### Create a circuit to cut" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5defa9fb-e928-4fcb-9ae3-bed8e8f2527b", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:57.096887Z", + "iopub.status.busy": "2025-03-28T16:50:57.096668Z", + "iopub.status.idle": "2025-03-28T16:50:58.481013Z", + "shell.execute_reply": "2025-03-28T16:50:58.480360Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.circuit.library import EfficientSU2\n", + "\n", + "qc = EfficientSU2(4, entanglement=\"linear\", reps=2).decompose()\n", + "qc.assign_parameters([0.4] * len(qc.parameters), inplace=True)\n", + "\n", + "qc.draw(\"mpl\", scale=0.8)" + ] + }, + { + "cell_type": "markdown", + "id": "99344052-a7cc-4857-a77e-0db02003cb7d", + "metadata": {}, + "source": [ + "### Specify the observables of interest" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "42c2d640-7cce-41c3-9683-920449215c9d", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:58.484791Z", + "iopub.status.busy": "2025-03-28T16:50:58.483701Z", + "iopub.status.idle": "2025-03-28T16:50:58.489908Z", + "shell.execute_reply": "2025-03-28T16:50:58.489261Z" + } + }, + "outputs": [], + "source": [ + "from qiskit.quantum_info import Pauli, SparsePauliOp\n", + "\n", + "observables = [\n", + " Pauli(\"-XZII\"),\n", + " SparsePauliOp([\"ZZII\", \"IZZI\", \"-IIZZ\", \"XIXI\", \"ZIZZ\", \"IXIX\"]),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "466343c4-35ce-496e-a887-5a126e0a6b40", + "metadata": {}, + "source": [ + "### Gather unique observable terms" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "defb33f4-820a-44aa-89ec-4f7eb13a49f5", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:58.492460Z", + "iopub.status.busy": "2025-03-28T16:50:58.491798Z", + "iopub.status.idle": "2025-03-28T16:50:58.561027Z", + "shell.execute_reply": "2025-03-28T16:50:58.560336Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_cutting.utils.observable_terms import gather_unique_observable_terms\n", + "\n", + "unique_observable_terms = gather_unique_observable_terms(observables)" + ] + }, + { + "cell_type": "markdown", + "id": "81c1c9e1-e8a4-4a5c-80f3-35d00298459f", + "metadata": {}, + "source": [ + "### Perform cutting workflow" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9830f036-904e-4900-98b1-7991da2915d0", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:58.568971Z", + "iopub.status.busy": "2025-03-28T16:50:58.568447Z", + "iopub.status.idle": "2025-03-28T16:50:58.579358Z", + "shell.execute_reply": "2025-03-28T16:50:58.578737Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_cutting import partition_problem\n", + "\n", + "partitioned_problem = partition_problem(\n", + " circuit=qc, partition_labels=\"AABB\", observables=unique_observable_terms\n", + ")\n", + "subcircuits = partitioned_problem.subcircuits\n", + "subobservables = partitioned_problem.subobservables" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "77577d6f-4acc-468b-962c-81a09cc31fcd", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:58.581924Z", + "iopub.status.busy": "2025-03-28T16:50:58.581453Z", + "iopub.status.idle": "2025-03-28T16:50:58.854801Z", + "shell.execute_reply": "2025-03-28T16:50:58.850920Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit_addon_cutting import generate_cutting_experiments\n", + "\n", + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits, observables=subobservables, num_samples=np.inf\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "37253e37-51f9-4aa4-9f5e-d8a032d639ce", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:58.859697Z", + "iopub.status.busy": "2025-03-28T16:50:58.859332Z", + "iopub.status.idle": "2025-03-28T16:50:59.766313Z", + "shell.execute_reply": "2025-03-28T16:50:59.765302Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime.fake_provider import FakeManilaV2\n", + "\n", + "backend = FakeManilaV2()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f15e198e-7f0b-4507-a73f-e4aab2f286ed", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:50:59.773825Z", + "iopub.status.busy": "2025-03-28T16:50:59.773001Z", + "iopub.status.idle": "2025-03-28T16:51:07.997499Z", + "shell.execute_reply": "2025-03-28T16:51:07.996531Z" + } + }, + "outputs": [], + "source": [ + "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", + "\n", + "# Transpile the subexperiments to ISA circuits\n", + "pass_manager = generate_preset_pass_manager(optimization_level=1, backend=backend)\n", + "isa_subexperiments = {\n", + " label: pass_manager.run(partition_subexpts)\n", + " for label, partition_subexpts in subexperiments.items()\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "5383d427-3285-43b1-8564-f451e1b4287e", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:08.002734Z", + "iopub.status.busy": "2025-03-28T16:51:08.001594Z", + "iopub.status.idle": "2025-03-28T16:51:08.452499Z", + "shell.execute_reply": "2025-03-28T16:51:08.444876Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime import SamplerV2, Batch\n", + "\n", + "# Submit each partition's subexperiments as a single batch\n", + "with Batch(backend=backend) as batch:\n", + " sampler = SamplerV2(mode=batch)\n", + " jobs = {\n", + " label: sampler.run(subsystem_subexpts, shots=2**12)\n", + " for label, subsystem_subexpts in isa_subexperiments.items()\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "154f4cf0-c45d-41b2-8091-b23a47b30c22", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:08.472013Z", + "iopub.status.busy": "2025-03-28T16:51:08.471699Z", + "iopub.status.idle": "2025-03-28T16:51:34.412339Z", + "shell.execute_reply": "2025-03-28T16:51:34.411608Z" + } + }, + "outputs": [], + "source": [ + "# Retrieve results\n", + "results = {label: job.result() for label, job in jobs.items()}" + ] + }, + { + "cell_type": "markdown", + "id": "35c57f33-a128-4b0f-9a23-705cfecae993", + "metadata": {}, + "source": [ + "### Reconstruct the expectation value of each unique term" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9f5e64a7-a334-4278-9c61-e78bbbc36368", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:34.415031Z", + "iopub.status.busy": "2025-03-28T16:51:34.414827Z", + "iopub.status.idle": "2025-03-28T16:51:39.000491Z", + "shell.execute_reply": "2025-03-28T16:51:38.999738Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_cutting import reconstruct_expectation_values\n", + "\n", + "# Get expectation values for each observable term\n", + "reconstructed_term_expvals = reconstruct_expectation_values(\n", + " results,\n", + " coefficients,\n", + " subobservables, # or unique_observable_terms if the circuit did not separate\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "792853df-d4f9-43d1-9f7d-50e38768ceb8", + "metadata": {}, + "source": [ + "### Reconstruct the expectation value of the original operators" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b134b767-ce53-4438-866f-a25952cb985b", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:39.003144Z", + "iopub.status.busy": "2025-03-28T16:51:39.002785Z", + "iopub.status.idle": "2025-03-28T16:51:39.006758Z", + "shell.execute_reply": "2025-03-28T16:51:39.006291Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_addon_cutting.utils.observable_terms import (\n", + " reconstruct_observable_expvals_from_terms,\n", + ")\n", + "\n", + "reconstructed_expvals = reconstruct_observable_expvals_from_terms(\n", + " observables, dict(zip(unique_observable_terms, reconstructed_term_expvals))\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fd71a9ef-37cd-4687-9fe1-9b4ea3264f91", + "metadata": {}, + "source": [ + "### Compare the reconstructed expectation values with the exact expectation value from the original circuit and observables" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "986e5ce8-64a5-464a-9c43-45ccf1a65b2d", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:39.008795Z", + "iopub.status.busy": "2025-03-28T16:51:39.008594Z", + "iopub.status.idle": "2025-03-28T16:51:39.023519Z", + "shell.execute_reply": "2025-03-28T16:51:39.022762Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reconstructed expectation values: [-0.20063001 0.48694563]\n", + "Exact expectation values: [-0.17994235 0.56254612]\n", + "Error in estimation: [-0.02068766 -0.07560049]\n", + "Relative error in estimation: [ 0.11496824 -0.13438986]\n" + ] + } + ], + "source": [ + "from qiskit_aer.primitives import EstimatorV2\n", + "\n", + "estimator = EstimatorV2()\n", + "exact_expvals = estimator.run([(qc, observables)]).result()[0].data.evs\n", + "print(\n", + " f\"Reconstructed expectation values: {np.real(np.round(reconstructed_expvals, 8))}\"\n", + ")\n", + "print(f\"Exact expectation values: {np.round(exact_expvals, 8)}\")\n", + "print(\n", + " f\"Error in estimation: {np.real(np.round(reconstructed_expvals-exact_expvals, 8))}\"\n", + ")\n", + "print(\n", + " f\"Relative error in estimation: {np.real(np.round((reconstructed_expvals-exact_expvals) / exact_expvals, 8))}\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires.ipynb b/docs/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires.ipynb new file mode 100644 index 00000000000..fb514a7b245 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires.ipynb @@ -0,0 +1,517 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to place wire cuts using a single-qubit CutWire instruction\"\n", + "description: \"How to place wire cuts using a single-qubit CutWire instruction for the latest version of Circuit cutting\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "b04b8bc7", + "metadata": {}, + "source": [ + "# How to place wire cuts using a single-qubit `CutWire` instruction\n", + "\n", + "This how-to guide is intended to demonstrate how to place wire cuts using single-qubit `CutWire` instructions, which may at times be more convenient than specifying them as two-qubit `Move` instructions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "1aa871cb", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:41.715866Z", + "iopub.status.busy": "2025-03-28T16:51:41.715618Z", + "iopub.status.idle": "2025-03-28T16:51:42.298891Z", + "shell.execute_reply": "2025-03-28T16:51:42.298034Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit import QuantumCircuit\n", + "from qiskit.quantum_info import SparsePauliOp\n", + "from qiskit_aer.primitives import EstimatorV2, SamplerV2\n", + "\n", + "from qiskit_addon_cutting import (\n", + " partition_problem,\n", + " generate_cutting_experiments,\n", + " reconstruct_expectation_values,\n", + ")\n", + "\n", + "from qiskit_addon_cutting.instructions import CutWire\n", + "from qiskit_addon_cutting import cut_wires, expand_observables" + ] + }, + { + "cell_type": "markdown", + "id": "06ae4c39", + "metadata": {}, + "source": [ + "### Prepare a circuit for cutting\n", + "\n", + "As in the [tutorial for wire cutting](../tutorials/03_wire_cutting_via_move_instruction.ipynb), we have used a circuit inspired by Fig. 1(a) of [arXiv:2302.03366v1](https://arxiv.org/abs/2302.03366v1). The cut locations are marked manually here with `CutWire` instructions." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0ae22516", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:42.302264Z", + "iopub.status.busy": "2025-03-28T16:51:42.301906Z", + "iopub.status.idle": "2025-03-28T16:51:43.264296Z", + "shell.execute_reply": "2025-03-28T16:51:43.263022Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc_0 = QuantumCircuit(7)\n", + "for i in range(7):\n", + " qc_0.rx(np.pi / 4, i)\n", + "qc_0.cx(0, 3)\n", + "qc_0.cx(1, 3)\n", + "qc_0.cx(2, 3)\n", + "qc_0.append(CutWire(), [3])\n", + "qc_0.cx(3, 4)\n", + "qc_0.cx(3, 5)\n", + "qc_0.cx(3, 6)\n", + "qc_0.append(CutWire(), [3])\n", + "qc_0.cx(0, 3)\n", + "qc_0.cx(1, 3)\n", + "qc_0.cx(2, 3)\n", + "qc_0.draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "7ee07a81", + "metadata": {}, + "source": [ + "### Recover the uncut circuit\n", + "\n", + "`CutWire` instructions decompose to nothing (they are equivalent to the identity), so the uncut circuit can be recovered as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "631286a6", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.267141Z", + "iopub.status.busy": "2025-03-28T16:51:43.266751Z", + "iopub.status.idle": "2025-03-28T16:51:43.652437Z", + "shell.execute_reply": "2025-03-28T16:51:43.651574Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc_0.decompose(\"cut_wire\").draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "dcd8dea0", + "metadata": {}, + "source": [ + "### Specify an observable\n", + "\n", + "This observable has 7 qubits, just like the original circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "847a3205", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.655017Z", + "iopub.status.busy": "2025-03-28T16:51:43.654586Z", + "iopub.status.idle": "2025-03-28T16:51:43.658036Z", + "shell.execute_reply": "2025-03-28T16:51:43.657602Z" + } + }, + "outputs": [], + "source": [ + "observable = SparsePauliOp([\"ZIIIIII\", \"IIIZIII\", \"IIIIIIZ\"])" + ] + }, + { + "cell_type": "markdown", + "id": "59730746", + "metadata": {}, + "source": [ + "### Transform cuts to moves\n", + "\n", + "The next step is to call `cut_wires`, which transforms each `CutWire` into a `TwoQubitQPDGate` which wraps a `Move` instruction. An additional qubit is added to the circuit for each `CutWire` in the input circuit.\n", + "\n", + "Notice that, unlike in the [wire cutting tutorial](../tutorials/03_wire_cutting_via_move_instruction.ipynb), where `Move` operations were placed manually, this function does not result in the _re_-use of a qubit. Because any method for qubit re-use is based on heuristics, this function naively allocates an additional qubit for each cut. Users wishing to re-use qubits might wish to experiment with [qiskit-qubit-reuse](https://github.com/qiskit-community/qiskit-qubit-reuse)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e4ee1559", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.660083Z", + "iopub.status.busy": "2025-03-28T16:51:43.659877Z", + "iopub.status.idle": "2025-03-28T16:51:43.924630Z", + "shell.execute_reply": "2025-03-28T16:51:43.924082Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc_1 = cut_wires(qc_0)\n", + "qc_1.draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "c22b9572", + "metadata": {}, + "source": [ + "### Update the observable terms to account for the extra qubit\n", + "\n", + "The transformed circuit contains additional qubits (one for each `CutWire` instruction), so the observables must be updated for the new circuit. This can be done using the `expand_observables` function. Since the observable coefficients are ignored until reconstruction of the final expectation value, the input and output types of the observables for ``expand_observables`` are ``PauliList``s.\n", + "\n", + "The resulting observables have 9 qubits, just like the transformed circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "95fbeda0", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.926980Z", + "iopub.status.busy": "2025-03-28T16:51:43.926767Z", + "iopub.status.idle": "2025-03-28T16:51:43.931899Z", + "shell.execute_reply": "2025-03-28T16:51:43.931437Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PauliList(['ZIIIIIIII', 'IIIZIIIII', 'IIIIIIIIZ'])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "observable_expanded_paulis = expand_observables(observable.paulis, qc_0, qc_1)\n", + "observable_expanded_paulis" + ] + }, + { + "cell_type": "markdown", + "id": "6f64acd6", + "metadata": {}, + "source": [ + "### Separate the circuit and observables\n", + "\n", + "In this case, `partition_labels` need not be passed to `partition_problem`, as the labels can be determined automatically from the connectivity of the circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "99bef123", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.933905Z", + "iopub.status.busy": "2025-03-28T16:51:43.933725Z", + "iopub.status.idle": "2025-03-28T16:51:43.940544Z", + "shell.execute_reply": "2025-03-28T16:51:43.940042Z" + } + }, + "outputs": [], + "source": [ + "partitioned_problem = partition_problem(\n", + " circuit=qc_1, observables=observable_expanded_paulis\n", + ")\n", + "subcircuits = partitioned_problem.subcircuits\n", + "subobservables = partitioned_problem.subobservables" + ] + }, + { + "cell_type": "markdown", + "id": "ce2bf0cd", + "metadata": {}, + "source": [ + "From here forward, the cutting workflow is the same as usual, but the remaining steps are spelled out here explicitly so one can follow along with the results." + ] + }, + { + "cell_type": "markdown", + "id": "bae9ac63", + "metadata": {}, + "source": [ + "### Visualize the decomposed problem\n", + "\n", + "Notice that once the circuits have been cut, some of the instructions are able to commute past each other. For instance, in subcircuit \"A\", half of the second `Move` operation is actually the _first_ operation on the final qubit." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "abeee650", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.942618Z", + "iopub.status.busy": "2025-03-28T16:51:43.942418Z", + "iopub.status.idle": "2025-03-28T16:51:43.946860Z", + "shell.execute_reply": "2025-03-28T16:51:43.946404Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: PauliList(['IIIII', 'ZIIII', 'IIIIZ']),\n", + " 1: PauliList(['ZIII', 'IIII', 'IIII'])}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subobservables" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "aaef5b3d", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:43.948780Z", + "iopub.status.busy": "2025-03-28T16:51:43.948581Z", + "iopub.status.idle": "2025-03-28T16:51:44.164480Z", + "shell.execute_reply": "2025-03-28T16:51:44.163535Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subcircuits[0].draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "975a3ca9", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:44.167652Z", + "iopub.status.busy": "2025-03-28T16:51:44.167358Z", + "iopub.status.idle": "2025-03-28T16:51:44.386632Z", + "shell.execute_reply": "2025-03-28T16:51:44.385952Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subcircuits[1].draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "2e02632b", + "metadata": {}, + "source": [ + "### Generate and run the cutting experiments; reconstruct and compare against uncut expectation values" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "459dcee8", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:44.389065Z", + "iopub.status.busy": "2025-03-28T16:51:44.388801Z", + "iopub.status.idle": "2025-03-28T16:51:44.496881Z", + "shell.execute_reply": "2025-03-28T16:51:44.496185Z" + } + }, + "outputs": [], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=np.inf,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "dc9fe287", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:44.501113Z", + "iopub.status.busy": "2025-03-28T16:51:44.500852Z", + "iopub.status.idle": "2025-03-28T16:51:45.949307Z", + "shell.execute_reply": "2025-03-28T16:51:45.948450Z" + } + }, + "outputs": [], + "source": [ + "sampler = SamplerV2()\n", + "results = {\n", + " label: sampler.run(subexperiment, shots=2**12).result()\n", + " for label, subexperiment in subexperiments.items()\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e317a998", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:45.952706Z", + "iopub.status.busy": "2025-03-28T16:51:45.951971Z", + "iopub.status.idle": "2025-03-28T16:51:49.139646Z", + "shell.execute_reply": "2025-03-28T16:51:49.138857Z" + } + }, + "outputs": [], + "source": [ + "reconstructed_expval_terms = reconstruct_expectation_values(\n", + " results,\n", + " coefficients,\n", + " subobservables,\n", + ")\n", + "reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5ae568ca", + "metadata": { + "execution": { + "iopub.execute_input": "2025-03-28T16:51:49.143349Z", + "iopub.status.busy": "2025-03-28T16:51:49.143053Z", + "iopub.status.idle": "2025-03-28T16:51:49.158786Z", + "shell.execute_reply": "2025-03-28T16:51:49.158077Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reconstructed expectation value: 1.62048799\n", + "Exact expectation value: 1.59099026\n", + "Error in estimation: 0.02949773\n", + "Relative error in estimation: 0.01854048\n" + ] + } + ], + "source": [ + "estimator = EstimatorV2()\n", + "exact_expval = (\n", + " estimator.run([(qc_0.decompose(\"cut_wire\"), observable)]).result()[0].data.evs\n", + ")\n", + "print(f\"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}\")\n", + "print(f\"Exact expectation value: {np.round(exact_expval, 8)}\")\n", + "print(f\"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}\")\n", + "print(\n", + " f\"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/addons/qiskit-addon-cutting/how-tos/index.mdx b/docs/addons/qiskit-addon-cutting/how-tos/index.mdx new file mode 100644 index 00000000000..2fe97d4f684 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/how-tos/index.mdx @@ -0,0 +1,28 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Circuit cutting" +--- + +# How-To Guides + +* [Generate exact quasi-distributions](how-to-generate-exact-quasi-dists-from-sampler): Use the [`ExactSampler`](/docs/api/qiskit-addon-cutting/utils-simulation#qiskit_addon_cutting.utils.simulation.ExactSampler "qiskit_addon_cutting.utils.simulation.ExactSampler") interface to generate exact quasi-distributions for circuits containing mid-circuit measurements. +* [Generate exact sampling coefficients](how-to-generate-exact-sampling-coefficients): Generate exact sampling coefficients and run all unique samples from the distribution. +* [Specify cut wires as a single-qubit instruction](how-to-specify-cut-wires): Perform wire cutting with a single-qubit CutWire instruction, rather than a two-qubit Move operation. +* [Perform a cutting workflow with multiple observables](how-to-provide-multiple-observables): Use the [`observable_terms`](/docs/api/qiskit-addon-cutting/utils-observable-terms#module-qiskit_addon_cutting.utils.observable_terms "qiskit_addon_cutting.utils.observable_terms") utility functions to gather unique observable terms, perform circuit cutting experiments, and reconstruct the desired expectation values. + +[![](/docs/images/addons/qiskit-addon-cutting/nbsphinx-no-thumbnail.svg)](how-to-generate-exact-quasi-dists-from-sampler) + +[How to generate exact quasiprobability distributions from Sampler](how-to-generate-exact-quasi-dists-from-sampler) + +[![](/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_generate_exact_sampling_coefficients_5_0.avif)](how-to-generate-exact-sampling-coefficients) + +[How to generate exact sampling coefficients](how-to-generate-exact-sampling-coefficients) + +[![](/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_provide_multiple_observables_2_0.avif)](how-to-provide-multiple-observables) + +[How to perform a cutting workflow with multiple observables](how-to-provide-multiple-observables) + +[![](/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_specify_cut_wires_18_0.avif)](how-to-specify-cut-wires) + +[How to place wire cuts using a single-qubit CutWire instruction](how-to-specify-cut-wires) + diff --git a/docs/addons/qiskit-addon-cutting/index.mdx b/docs/addons/qiskit-addon-cutting/index.mdx new file mode 100644 index 00000000000..76ddbd9a524 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/index.mdx @@ -0,0 +1,127 @@ +--- +title: "Qiskit addon: circuit cutting" +description: "Documentation for the latest version of Circuit cutting" +--- + +# Qiskit addon: circuit cutting + +[![GitHub repository star counter badge](https://img.shields.io/github/stars/Qiskit/qiskit-addon-cutting?style=social)](https://github.com/Qiskit/qiskit-addon-cutting) + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package implements circuit cutting. In this technique, a handful of gates and/or wires are cut, resulting in smaller circuits that are better suited for execution on hardware. The result of the original circuit can then be reconstructed; however, the trade-off is that the overall number of shots must be increased by a factor exponential in the number of cuts. + +For a more detailed discussion on circuit cutting, check out our [technical guide](./circuit_cutting/explanation/index-rst#overview-of-circuit-cutting). + +We follow [semantic versioning](https://semver.org/) and are 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-cutting/release-notes#release-notes). + + + This package was known as the Circuit Knitting Toolbox prior to September 2024. + + +## Citing this project + +If you use this package in your research, please cite it according to `CITATON.bib` file included in this repository: + +```bibtex +@misc{qiskit-addon-cutting, + author = { + Agata M. Bra\'{n}czyk + and Almudena {Carrera Vazquez} + and Daniel J. Egger + and Bryce Fuller + and Julien Gacon + and James R. Garrison + and Jennifer R. Glick + and Caleb Johnson + and Saasha Joshi + and Edwin Pednault + and C. D. Pemmaraju + and Pedro Rivero + and Ibrahim Shehzad + and Stefan Woerner + }, + title = {{Qiskit addon: circuit cutting}}, + howpublished = {\url{https://github.com/Qiskit/qiskit-addon-cutting}}, + year = {2024}, + doi = {10.5281/zenodo.7987997} +} +``` + +If you are using the entanglement forging tool in Circuit Knitting Toolbox version 0.5 or earlier, please use [an older version of the citation file](https://github.com/Qiskit/qiskit-addon-cutting/blob/stable/0.5/CITATION.bib) which includes the authors of that tool. + +If you are using the CutQC tool in Circuit Knitting Toolbox version 0.7 or earlier, please use [an older version of the citation file](https://github.com/Qiskit/qiskit-addon-cutting/blob/stable/0.7/CITATION.bib) which includes the authors of that tool. + +## Developer guide + +The source code to this package is available [on GitHub](https://github.com/Qiskit/qiskit-addon-cutting). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-cutting/blob/main/CONTRIBUTING.md) in the root of this project’s repository. + +## Contents + +* [Documentation Home]() + +* [Installation Instructions](install) + + * [Pre-Installation](install#pre-installation) + * [Option 1: Install from PyPI](install#option-1-install-from-pypi) + * [Option 2: Install from Source](install#option-2-install-from-source) + * [Option 3: Use within Docker](install#option-3-use-within-docker) + * [Platform Support](install#platform-support) + +* [Tutorials](tutorials/index) + + * [Gate Cutting to Reduce Circuit Width](tutorials/01-gate-cutting-to-reduce-circuit-width) + * [Gate Cutting to Reduce Circuit Depth](tutorials/02-gate-cutting-to-reduce-circuit-depth) + * [Wire Cutting Phrased as a Two-Qubit `Move` Instruction](tutorials/03-wire-cutting-via-move-instruction) + * [Automatically find cuts](tutorials/04-automatic-cut-finding) + +* [Explanatory Material](explanation/index) + + * [Overview of circuit cutting](explanation/index#overview-of-circuit-cutting) + * [Key terms](explanation/index#key-terms) + * [Circuit cutting as a quasiprobability decomposition (QPD)](explanation/index#circuit-cutting-as-a-quasiprobability-decomposition-qpd) + * [An example: cutting a `RZZGate`](explanation/index#an-example-cutting-a-rzzgate) + * [More general cut two-qubit gates via the KAK decomposition](explanation/index#more-general-cut-two-qubit-gates-via-the-kak-decomposition) + * [Wire cutting phrased as a two-qubit `Move` operation](explanation/index#wire-cutting-phrased-as-a-two-qubit-move-operation) + * [Sample weights in the Qiskit addon for circuit cutting](explanation/index#sample-weights-in-the-qiskit-addon-for-circuit-cutting) + * [Sampling overhead reference table](explanation/index#sampling-overhead-reference-table) + * [Current limitations](explanation/index#current-limitations) + * [References](explanation/index#references) + +* [How-To Guides](how-tos/index) + + * [How to generate exact quasiprobability distributions from Sampler](how-tos/how-to-generate-exact-quasi-dists-from-sampler) + * [How to generate exact sampling coefficients](how-tos/how-to-generate-exact-sampling-coefficients) + * [How to perform a cutting workflow with multiple observables](how-tos/how-to-provide-multiple-observables) + * [How to place wire cuts using a single-qubit `CutWire` instruction](how-tos/how-to-specify-cut-wires) + +* [API References](/docs/api/qiskit-addon-cutting/index) + + * [Circuit cutting (`qiskit_addon_cutting`)](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting) + * [Instructions (`qiskit_addon_cutting.instructions`)](/docs/api/qiskit-addon-cutting/instructions) + * [Quasi-Probability Decomposition (QPD) (`qiskit_addon_cutting.qpd`)](/docs/api/qiskit-addon-cutting/qpd) + * [Bitwise utilities (`qiskit_addon_cutting.utils.bitwise`)](/docs/api/qiskit-addon-cutting/utils-bitwise) + * [Iteration utilities (`qiskit_addon_cutting.utils.iteration`)](/docs/api/qiskit-addon-cutting/utils-iteration) + * [Observable grouping (`qiskit_addon_cutting.utils.observable_grouping`)](/docs/api/qiskit-addon-cutting/utils-observable-grouping) + * [Observable terms (`qiskit_addon_cutting.utils.observable_terms`)](/docs/api/qiskit-addon-cutting/utils-observable-terms) + * [Simulation utilities (`qiskit_addon_cutting.utils.simulation`)](/docs/api/qiskit-addon-cutting/utils-simulation) + * [Transforms (`qiskit_addon_cutting.utils.transforms`)](/docs/api/qiskit-addon-cutting/utils-transforms) + * [Transpiler passes (`qiskit_addon_cutting.utils.transpiler_passes`)](/docs/api/qiskit-addon-cutting/utils-transpiler-passes) + +* [GitHub](https://github.com/Qiskit/qiskit-addon-cutting) + +* [Release Notes](/docs/api/qiskit-addon-cutting/release-notes) + + * [0.10.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-10-0) + * [0.9.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-9-0) + * [0.7.1](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-7-1) + * [0.7.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-7-0) + * [0.6.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-6-0) + * [0.5.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-5-0) + * [0.4.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-4-0) + * [0.3.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-3-0) + * [0.2.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-2-0) + * [0.1.0](/docs/api/qiskit-addon-cutting/release-notes#release-notes-0-1-0) + diff --git a/docs/addons/qiskit-addon-cutting/install.mdx b/docs/addons/qiskit-addon-cutting/install.mdx new file mode 100644 index 00000000000..5cf283e5dc4 --- /dev/null +++ b/docs/addons/qiskit-addon-cutting/install.mdx @@ -0,0 +1,128 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Circuit cutting" +--- + +# Installation Instructions + +Let’s see how to install the Qiskit addon for circuit cutting. The first thing to do is choose how you’re going to run and install the packages. There are three primary ways to do this: + +* [Option 1: Install from PyPI](#option-1) +* [Option 2: Install from Source](#option-2) +* [Option 3: Use within Docker](#option-3) + +Users who wish to run within a containerized environment may skip the pre-installation and move straight to [Option 3: Use within Docker](#option-3). + +## Pre-Installation + +Users who wish to install locally (using either [Option 1: Install from PyPI](#option-1) or [Option 2: Install from Source](#option-2)) are encouraged to follow a brief set of common instructions to prepare a Python environment for installation: + +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-cutting` package is via PyPI. + +```sh +pip install --upgrade pip +pip install qiskit-addon-cutting +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +In either case, the first step is to clone the `qiskit-addon-cutting` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-cutting.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-cutting +``` + +The next step is to install to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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 with the addon by running the notebooks in the docs. + +```python +cd docs/ +jupyter notebook +``` + + + +## Option 3: Use within Docker + +We have provided a [Dockerfile](https://github.com/Qiskit/qiskit-addon-cutting/blob/main/Dockerfile), which can be used to build a Docker image, as well as a [compose.yaml](https://github.com/Qiskit/qiskit-addon-cutting/blob/main/compose.yaml) file, which allows one to use the Docker image with just a few simple commands. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-cutting.git +cd qiskit-addon-cutting +docker compose build +docker compose up +``` + +Depending on your system configuration, you may need to type `sudo` before each `docker compose` command. + + + If you are instead using [podman](https://podman.io/) and [podman-compose](https://github.com/containers/podman-compose), the commands are: + + ```sh + podman machine start + podman-compose --podman-pull-args short-name-mode="permissive" build + podman-compose up + ``` + + +Once the container is running, you should see a message like this: + +```python +notebook_1 | To access the server, open this file in a browser: +notebook_1 | file:///home/jovyan/.local/share/jupyter/runtime/jpserver-7-open.html +notebook_1 | Or copy and paste one of these URLs: +notebook_1 | http://e4a04564eb39:8888/lab?token=00ed70b5342f79f0a970ee9821c271eeffaf760a7dcd36ec +notebook_1 | or http://127.0.0.1:8888/lab?token=00ed70b5342f79f0a970ee9821c271eeffaf760a7dcd36ec +``` + +Locate the *last* URL in your terminal (the one that includes `127.0.0.1`), and navigate to that URL in a web browser to access the Jupyter Notebook interface. + +The home directory includes a subdirectory named `persistent-volume`. All work you’d like to save should be placed in this directory, as it is the only one that will be saved across different container runs. + + + +## Platform Support + +We expect this package to work on [any platform supported by Qiskit](/docs/start/install#operating-system-support). If you are experiencing issues running the software on your device, you may consider [using this package within Docker](#option-3). + diff --git a/docs/addons/qiskit-addon-mpf/_toc.json b/docs/addons/qiskit-addon-mpf/_toc.json new file mode 100644 index 00000000000..95724645ec9 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/_toc.json @@ -0,0 +1,68 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Multi-product formulas (MPF) 0.3", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-mpf" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-mpf/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "How to choose the Trotter steps for an MPF", + "url": "/docs/addons/qiskit-addon-mpf/how_tos/choose-trotter-steps" + }, + { + "title": "How to use the approximate model", + "url": "/docs/addons/qiskit-addon-mpf/how_tos/using-approximate-model" + } + ] + }, + { + "title": "Explanations", + "children": [ + { + "title": "Understanding the stability of MPFs", + "url": "/docs/addons/qiskit-addon-mpf/explanations/mpf-stability" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-mpf" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "collapsible": false, + "children": [ + { + "title": "Multi-product formulas to reduce Trotter error", + "url": "/docs/tutorials/multi-product-formula" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Multi-product formulas (MPF) API reference", + "url": "/docs/api/qiskit-addon-mpf" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-mpf/explanations/index.mdx b/docs/addons/qiskit-addon-mpf/explanations/index.mdx new file mode 100644 index 00000000000..59ad6ab7b20 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/explanations/index.mdx @@ -0,0 +1,11 @@ +--- +title: "Explanations" +description: "Explanations for the latest version of Multi-product formulas (MPF)" +--- + +# Explanations + +This page summarizes additional explanatory content. + +* [Understanding the stability of MPFs](mpf-stability) + diff --git a/docs/addons/qiskit-addon-mpf/explanations/mpf-stability.ipynb b/docs/addons/qiskit-addon-mpf/explanations/mpf-stability.ipynb new file mode 100644 index 00000000000..271ae4fde5f --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/explanations/mpf-stability.ipynb @@ -0,0 +1,318 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Understanding the stability of MPFs\"\n", + "description: \"Understanding the stability of MPFs for the latest version of Multi-product formulas (MPF)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "4aaf8397-4140-4d16-b4b5-1d9e20adea98", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# Understanding the stability of MPFs\n", + "\n", + "Over in the guide [How to choose the Trotter steps for an MPF](https://qiskit.github.io/qiskit-addon-mpf/how_tos/choose_trotter_steps.html) we introduced a heuristic that limits the **smallest** $k_j$ value ($k_{\\text{min}}$) based on the total evolution time, $t$, that we would like to reach.\n", + "In particular, we state that $t/k_{\\text{min}} \\lt 1$ must be satisfied (noting that $t/k_{\\text{min}} \\leq 1$ empirically seems to work fine, too).\n", + "\n", + "On this page, we will analyze when the behavior of MPFs is not guaranteed to work well, such as when this constraint gets violated." + ] + }, + { + "cell_type": "markdown", + "id": "986a58b5-611a-4400-9bd5-ba35d4a14188", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Setting up a simple model problem\n", + "\n", + "For this simple example, we will reuse the model problem from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial; the Ising model on a line of 10 sites:\n", + "\n", + "$$\n", + "\\hat{\\mathcal{H}}_{\\text{Ising}} = \\sum_{i=1}^{9} J_{i,(i+1)} Z_i Z_{(i+1)} + \\sum_{i=1}^{10} h_i X_i \\, ,\n", + "$$\n", + "\n", + "where $J$ is the coupling strength between two sites and $h$ is the external magnetic field." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d77e337b-0a69-49a5-b449-ffe4ad17d7ab", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['IIIIIIIZZI', 'IIIIIZZIII', 'IIIZZIIIII', 'IZZIIIIIII', 'IIIIIIIIZZ', 'IIIIIIZZII', 'IIIIZZIIII', 'IIZZIIIIII', 'ZZIIIIIIII', 'IIIIIIIIIX', 'IIIIIIIIXI', 'IIIIIIIXII', 'IIIIIIXIII', 'IIIIIXIIII', 'IIIIXIIIII', 'IIIXIIIIII', 'IIXIIIIIII', 'IXIIIIIIII', 'XIIIIIIIII'],\n", + " coeffs=[1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j,\n", + " 1. +0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j,\n", + " 0.4+0.j, 0.4+0.j, 0.4+0.j])\n" + ] + } + ], + "source": [ + "from qiskit.transpiler import CouplingMap\n", + "from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian\n", + "\n", + "coupling_map = CouplingMap.from_line(10, bidirectional=False)\n", + "\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " coupling_map,\n", + " coupling_constants=(0.0, 0.0, 1.0),\n", + " ext_magnetic_field=(0.4, 0.0, 0.0),\n", + ")\n", + "print(hamiltonian)" + ] + }, + { + "cell_type": "markdown", + "id": "0f25e279-e987-4da5-a7e3-ffea41542d51", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The observable that we will be measuring is the total magnetization which we can simply construct as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "eca935e2-206e-471e-a673-62a67a00add0", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['IIIIIIIIIZ', 'IIIIIIIIZI', 'IIIIIIIZII', 'IIIIIIZIII', 'IIIIIZIIII', 'IIIIZIIIII', 'IIIZIIIIII', 'IIZIIIIIII', 'IZIIIIIIII', 'ZIIIIIIIII'],\n", + " coeffs=[0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j,\n", + " 0.05+0.j, 0.05+0.j, 0.05+0.j])\n" + ] + } + ], + "source": [ + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "L = coupling_map.size()\n", + "observable = SparsePauliOp.from_sparse_list([(\"Z\", [i], 1 / L / 2) for i in range(L)], num_qubits=L)\n", + "print(observable)" + ] + }, + { + "cell_type": "markdown", + "id": "b652e4ab-26c1-4c4e-a34d-47a464c60f26", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Simulating various Trotter circuits\n", + "\n", + "Our analysis simply consists of comparing the behavior of product formulas of various order (here, second and fourth order) to the exact solution (which we can compute for a problem as simple as this one).\n", + "\n", + "In the code cell below, we do exactly that, for evolution times ranging from $2$ up to $10$ and Trotter steps ranging from $1$ to $25$." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e7c43eec-3809-4e3a-998a-7f6a7a8cf2a6", + "metadata": { + "editable": true, + "scrolled": true, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "skip-execution" + ] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.primitives import StatevectorEstimator\n", + "from qiskit.synthesis import SuzukiTrotter\n", + "from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit\n", + "from scipy.linalg import expm\n", + "\n", + "estimator = StatevectorEstimator()\n", + "times = np.array(range(2, 11))\n", + "ks = np.array(range(1, 25))\n", + "\n", + "exact = []\n", + "order2 = []\n", + "order4 = []\n", + "\n", + "for time in times:\n", + " exp_H = expm(-1j * time * hamiltonian.to_matrix())\n", + " initial_state = np.zeros(exp_H.shape[0])\n", + " initial_state[0] = 1.0\n", + " time_evolved_state = exp_H @ initial_state\n", + " exact_evs = time_evolved_state.conj() @ observable.to_matrix() @ time_evolved_state\n", + " exact.append(float(exact_evs))\n", + "\n", + " for k in ks:\n", + " order2_circ = generate_time_evolution_circuit(\n", + " hamiltonian, synthesis=SuzukiTrotter(reps=k, order=2), time=time\n", + " )\n", + " order4_circ = generate_time_evolution_circuit(\n", + " hamiltonian, synthesis=SuzukiTrotter(reps=k, order=4), time=time\n", + " )\n", + "\n", + " job = estimator.run([(order2_circ, observable), (order4_circ, observable)])\n", + " result = job.result()\n", + " order2.append(result[0].data.evs)\n", + " order4.append(result[1].data.evs)" + ] + }, + { + "cell_type": "markdown", + "id": "0da8acd2-6192-450e-99b3-e0d2b82541ee", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Analyzing the results\n", + "\n", + "We can now analyze the computed results by plotting them." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "845cf113-58da-454e-97b6-679d5a5ae9fb", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "skip-execution" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "\n", + "order2_reshaped = np.asarray(order2).reshape((len(times), len(ks)))\n", + "order4_reshaped = np.asarray(order4).reshape((len(times), len(ks)))\n", + "\n", + "fig, axes = plt.subplots(3, 3, figsize=(20, 10))\n", + "\n", + "for i, t in enumerate(times):\n", + " ax = axes[i // 3, i % 3]\n", + "\n", + " ax.axvline(1.0, color=\"grey\", label=\"$t/k=1$\")\n", + " ax.axhline(exact[i], color=\"tab:green\", label=\"exact\")\n", + "\n", + " ax.plot(t / ks, order2_reshaped[i], label=r\"$\\chi=2$\")\n", + " ax.plot(t / ks, order4_reshaped[i], label=r\"$\\chi=4$\")\n", + "\n", + " ax.semilogx()\n", + " ax.set_xticks([10**p for p in range(-1, 1)])\n", + " ax.set_xlabel(\"$t/k$\")\n", + "\n", + " ax.set_ylim([-0.33, 0.55])\n", + " ax.set_ylabel(r\"$\\langle O \\rangle$\")\n", + "\n", + " ax.set_title(f\"time={t}\")\n", + "\n", + "fig.legend(*ax.get_legend_handles_labels(), ncols=4, loc=\"center\", bbox_to_anchor=(0.5, 0.0))\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "2f40b1ee-0e98-4f24-9d25-080306ba98e1", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We can now clearly see that in the regime $t/k \\gt 1$ the dynamics are no longer reproduced faithfully by the Trotterized circuits. Thus, if an MPF were to use a product formula with a $k_j$ from that regime, its extrapolated expectation value cannot faithfully produce good results." + ] + } + ], + "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-mpf/how_tos/choose-trotter-steps.ipynb b/docs/addons/qiskit-addon-mpf/how_tos/choose-trotter-steps.ipynb new file mode 100644 index 00000000000..0496a1f3912 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/how_tos/choose-trotter-steps.ipynb @@ -0,0 +1,308 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to choose the Trotter steps for an MPF\"\n", + "description: \"How to choose the Trotter steps for an MPF for the latest version of Multi-product formulas (MPF)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "dafa4d64-0da8-4e18-aea8-73f13cac3d29", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# How to choose the Trotter steps for an MPF\n", + "\n", + "This guide teaches you how to find a good set of Trotter steps for an MPF.\n", + "We assume that you have already read and understood the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial." + ] + }, + { + "cell_type": "markdown", + "id": "fb022d51-3187-4822-8536-7778df8620f1", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Understanding some heuristics\n", + "\n", + "In principle, any $k_j$ values can be chosen but some choices of Trotter steps will lead to a larger noise amplification on real devices than others.\n", + "Thus, it is important that one tries to find _\"good\"_ values of $k_j$.\n", + "In the following we will discuss some heuristics to help guide you in doing so.\n", + "\n", + "1. In practice, our **largest** $k_j$ ($k_{\\text{max}}$) value is simply bound by the depth of quantum circuit we can reliably execute.\n", + "\n", + "2. Since the Trotter error is only well behaved for $\\text{d}t = t/k \\lt 1$ (see [Understanding the stability of MPFs](https://qiskit.github.io/qiskit-addon-mpf/explanations/mpf_stability.html)), our **smallest** $k_j$ ($k_{\\text{min}}$) should satisfy: $t/k_{\\text{min}} \\lt 1$ (Note: empirically $\\text{d}t \\leq 1$ seems to work fine, too).\n", + "\n", + "3. None of our $x_j$ values should be close to $0$, as this would imply that we might as well exclude the corresponding $k_j$ from the expansion.\n", + "\n", + "4. Similarly, we do not want the $x_j$ for $k_{\\text{max}}$ to be the only dominant one, since that implies that we are essentially just recovering a single product formula with $k=k_{\\text{max}}$.\n", + "\n", + "5. Finally, we want the L1-norm of our coefficients, $x_j$, to be small, as this indicates a well-conditioned MPF (see [Carrera Vazquez et al., 2023]). Furthermore, small coefficients minimize the variance amplification of the MPF.\n", + "\n", + "[Carrera Vazquez et al., 2023]: https://quantum-journal.org/papers/q-2023-07-25-1067/" + ] + }, + { + "cell_type": "markdown", + "id": "f0f2096f-5fb6-4861-a05e-3ded9ac65a82", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Applying these heuristics\n", + "\n", + "Reusing the example from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial, we want to simulate up to time $t=8.0$.\n", + "Thus, $k_{\\text{min}}=8$.\n", + "\n", + "For the sake of this example, let us assume that our deepest circuit is bound by $k_{\\text{max}}=20$.\n", + "\n", + "In addition to the constraints above, we will follow the heuristic presented by [Zhuk et al., 2023], where they minimize a re-weighted L1-norm of the coefficients:\n", + "\n", + "$$\n", + "\\text{min}_{k_j} \\sum_j \\frac{|c_j|}{k_j^{2\\chi}} \\, ,\n", + "$$\n", + "\n", + "where $\\chi$ is the order of our individual product formulas.\n", + "\n", + "[Zhuk et al., 2023]: https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.033309" + ] + }, + { + "cell_type": "markdown", + "id": "1ff9ff06-47bf-451d-815f-11d331cafaa3", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Preparing our LSE\n", + "\n", + "In a first step, we prepare the LSE, $Ax=b$, leveraging a feature of the [setup_static_lse](/docs/api/qiskit-addon-mpf/static#setup_static_lse) function that allows us to use a [cvxpy.Parameter](https://www.cvxpy.org/api_reference/cvxpy.expressions.html#parameter) object for our $k_j$ values." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "bda8c5d3-b48a-4c9e-bef3-fb9f26846ad6", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "order = 2\n", + "symmetric = True\n", + "\n", + "min_k = 8\n", + "max_k = 20\n", + "\n", + "max_l1_norm = 5.0\n", + "min_coeff = 0.01" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "3b68ec15-b8ed-45a1-bd46-ced7b29b159e", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import cvxpy as cp\n", + "from qiskit_addon_mpf.static import setup_static_lse\n", + "\n", + "ks = cp.Parameter(3, integer=True)\n", + "lse = setup_static_lse(ks, order=order, symmetric=symmetric)" + ] + }, + { + "cell_type": "markdown", + "id": "92356e74-97b6-4231-bd35-43d5cd274dca", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Next, we loop over all possible 3-tuples of $k_j$ values within our specified range.\n", + "For each one, we compute the analytical coefficients, $x_j$, and then apply our filter criteria.\n", + "We keep track of those $k_j$ tuples which satisfy our criteria." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "da4c0610-8d1c-4596-b082-8cfd0098bc7c", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "possible_ks = {}\n", + "\n", + "for k1 in range(min_k, max_k):\n", + " for k2 in range(k1 + 1, max_k):\n", + " for k3 in range(k2 + 1, max_k):\n", + " ks.value = [k1, k2, k3]\n", + " coeffs = lse.solve()\n", + "\n", + " if np.any(np.isclose(coeffs, 0.0, atol=min_coeff)):\n", + " continue\n", + "\n", + " norm1 = np.linalg.norm(coeffs, ord=1)\n", + " if norm1 > max_l1_norm:\n", + " continue\n", + "\n", + " weighted = np.sum([np.abs(c) / k ** (2 * order) for c, k in zip(coeffs, ks.value)])\n", + " possible_ks[tuple(int(k) for k in ks.value)] = {\n", + " \"coeffs\": tuple(coeffs),\n", + " \"weighted\": float(weighted),\n", + " \"norm1\": float(norm1),\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "9f37b658-0e73-409b-add2-4cbf83708730", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Finally, we sort our $k_j$ by the re-weighted L1-norm presented by [Zhuk et al., 2023]:\n", + "\n", + "[Zhuk et al., 2023]: https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.033309" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "16d05ed1-c1ed-445d-afce-543f55275e5e", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(8, 14, 19) {'coeffs': (0.10447913478216586, -1.7638200183654775, 2.6593408835833117), 'weighted': 9.182736455463762e-05, 'norm1': 4.527640036730955}\n", + "(8, 13, 19) {'coeffs': (0.13134519801186473, -1.4167162698412699, 2.285371071829405), 'weighted': 9.920634920634922e-05, 'norm1': 3.8334325396825397}\n", + "(8, 12, 19) {'coeffs': (0.17239057239057254, -1.1944700460829496, 2.022079473692377), 'weighted': 0.00011520737327188946, 'norm1': 3.388940092165899}\n", + "(9, 13, 19) {'coeffs': (0.2662743506493499, -1.6904000946969673, 2.4241257440476174), 'weighted': 0.00011837121212121191, 'norm1': 4.380800189393934}\n", + "(8, 13, 18) {'coeffs': (0.15003663003663004, -1.7549001536098316, 2.6048635235732016), 'weighted': 0.00012288786482334872, 'norm1': 4.509800307219663}\n", + "(8, 12, 18) {'coeffs': (0.19692307692307653, -1.4399999999999984, 2.243076923076922), 'weighted': 0.0001388888888888887, 'norm1': 3.879999999999997}\n", + "(8, 11, 19) {'coeffs': (0.24195168054817162, -1.070248538011695, 1.8282968574635234), 'weighted': 0.00014619883040935662, 'norm1': 3.14049707602339}\n", + "(9, 12, 19) {'coeffs': (0.37193877551020393, -1.5167873601053319, 2.144848584595128), 'weighted': 0.00014629507717065315, 'norm1': 4.033574720210664}\n", + "(8, 12, 17) {'coeffs': (0.22755555555555573, -1.7875862068965531, 2.5600306513409974), 'weighted': 0.00017241379310344843, 'norm1': 4.575172413793107}\n", + "(8, 11, 18) {'coeffs': (0.27638326585694917, -1.265318468585254, 1.988935202728305), 'weighted': 0.00017284590787313073, 'norm1': 3.530636937170508}\n", + "(9, 12, 18) {'coeffs': (0.42857142857142816, -1.8285714285714276, 2.3999999999999995), 'weighted': 0.00017636684303350958, 'norm1': 4.657142857142855}\n", + "(9, 11, 19) {'coeffs': (0.5858035714285708, -1.5251041666666654, 1.9393005952380946), 'weighted': 0.00020833333333333313, 'norm1': 4.05020833333333}\n", + "(8, 11, 17) {'coeffs': (0.3193762183235871, -1.5289264828738525, 2.2095502645502654), 'weighted': 0.00020885547201336693, 'norm1': 4.0578529657477045}\n", + "(8, 10, 19) {'coeffs': (0.383090160867938, -1.0642826734780744, 1.6811925126101364), 'weighted': 0.00021285653469561486, 'norm1': 3.1285653469561487}\n", + "(9, 11, 18) {'coeffs': (0.6750000000000009, -1.8030788177339916, 2.1280788177339907), 'weighted': 0.0002463054187192121, 'norm1': 4.606157635467984}\n", + "(8, 10, 18) {'coeffs': (0.43760683760683694, -1.2400793650793638, 1.8024725274725268), 'weighted': 0.0002480158730158727, 'norm1': 3.4801587301587276}\n", + "(8, 11, 16) {'coeffs': (0.3742690058479534, -1.9026640675763484, 2.528395061728395), 'weighted': 0.0002599090318388565, 'norm1': 4.805328135152697}\n", + "(8, 10, 17) {'coeffs': (0.5056790123456789, -1.4697236919459142, 1.9640446796002353), 'weighted': 0.00029394473838918284, 'norm1': 3.9394473838918285}\n", + "(8, 10, 16) {'coeffs': (0.592592592592593, -1.7806267806267817, 2.1880341880341887), 'weighted': 0.0003561253561253563, 'norm1': 4.561253561253563}\n", + "(8, 9, 19) {'coeffs': (0.8112497524262232, -1.3783613445378153, 1.5671115921115921), 'weighted': 0.00042016806722689084, 'norm1': 3.756722689075631}\n", + "(8, 9, 18) {'coeffs': (0.9266968325791858, -1.5882352941176474, 1.6615384615384616), 'weighted': 0.00048414427499394834, 'norm1': 4.176470588235295}\n", + "(8, 9, 17) {'coeffs': (1.0708496732026151, -1.855486425339368, 1.7846367521367528), 'weighted': 0.0005656108597285072, 'norm1': 4.710972850678736}\n" + ] + } + ], + "source": [ + "for ks_val in sorted(possible_ks, key=lambda ks_val: possible_ks[ks_val].get(\"weighted\")):\n", + " print(ks_val, possible_ks[ks_val])" + ] + }, + { + "cell_type": "markdown", + "id": "f3b709d3-ed27-4e6f-a2ba-f3f17326099e", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Recalling the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial, from these possible values we picked `ks=(8, 12, 19)` since it strikes a good balance of minimizing the re-weighted and plain L1-norms." + ] + } + ], + "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-mpf/how_tos/index.mdx b/docs/addons/qiskit-addon-mpf/how_tos/index.mdx new file mode 100644 index 00000000000..4405aeedc78 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/how_tos/index.mdx @@ -0,0 +1,12 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Multi-product formulas (MPF)" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +* [How to choose the Trotter steps for an MPF](choose-trotter-steps) +* [How to use the approximate model](using-approximate-model) + diff --git a/docs/addons/qiskit-addon-mpf/how_tos/using-approximate-model.ipynb b/docs/addons/qiskit-addon-mpf/how_tos/using-approximate-model.ipynb new file mode 100644 index 00000000000..1d28d55a8c3 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/how_tos/using-approximate-model.ipynb @@ -0,0 +1,1040 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How to use the approximate model\"\n", + "description: \"How to use the approximate model for the latest version of Multi-product formulas (MPF)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "8d428726-b143-45b7-9c23-d7aef8f3690b", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# How to use the approximate model\n", + "\n", + "You have already seen a first example of the [setup_sum_of_squares_problem](/docs/api/qiskit-addon-mpf/costs#setup_sum_of_squares_problem) function in the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial.\n", + "In this guide, we are going to revisit that function in more detail." + ] + }, + { + "cell_type": "markdown", + "id": "c9e6b25b-6f6f-49c6-84ac-9493c151a968", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Setting up a simple model problem\n", + "\n", + "For this guide, we will reuse the simple model problem from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial; the Ising model on a line of 10 sites:\n", + "\n", + "$$\n", + "\\hat{\\mathcal{H}}_{\\text{Ising}} = \\sum_{i=1}^{9} J_{i,(i+1)} Z_i Z_{(i+1)} + \\sum_{i=1}^{10} h_i X_i \\, ,\n", + "$$\n", + "\n", + "where $J$ is the coupling strength between two sites and $h$ is the external magnetic field." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3c8d6feb-d0fb-45ac-8bf6-213b29bcb8a4", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['IIIIIIIZZI', 'IIIIIZZIII', 'IIIZZIIIII', 'IZZIIIIIII', 'IIIIIIIIZZ', 'IIIIIIZZII', 'IIIIZZIIII', 'IIZZIIIIII', 'ZZIIIIIIII', 'IIIIIIIIIX', 'IIIIIIIIXI', 'IIIIIIIXII', 'IIIIIIXIII', 'IIIIIXIIII', 'IIIIXIIIII', 'IIIXIIIIII', 'IIXIIIIIII', 'IXIIIIIIII', 'XIIIIIIIII'],\n", + " coeffs=[1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j, 1. +0.j,\n", + " 1. +0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j, 0.4+0.j,\n", + " 0.4+0.j, 0.4+0.j, 0.4+0.j])\n" + ] + } + ], + "source": [ + "from qiskit.transpiler import CouplingMap\n", + "from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian\n", + "\n", + "coupling_map = CouplingMap.from_line(10, bidirectional=False)\n", + "\n", + "hamiltonian = generate_xyz_hamiltonian(\n", + " coupling_map,\n", + " coupling_constants=(0.0, 0.0, 1.0),\n", + " ext_magnetic_field=(0.4, 0.0, 0.0),\n", + ")\n", + "print(hamiltonian)" + ] + }, + { + "cell_type": "markdown", + "id": "46ab2bb9-fd98-4c59-8d9c-66993652ff13", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Choosing $k_j$\n", + "\n", + "In this guide, we are going to focus on the tweaks that we can apply to the [cvxpy.Problem](https://www.cvxpy.org/api_reference/cvxpy.problems.html#cvxpy.Problem) and its solver that are returned by the [setup_sum_of_squares_problem](/docs/api/qiskit-addon-mpf/costs#setup_sum_of_squares_problem). Therefore, we are not concerned with choosing any specific $k_j$ values and can simply reuse the ones from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2ec34c2f-1f6f-4346-9ba6-f7f1c0e3620e", + "metadata": {}, + "outputs": [], + "source": [ + "trotter_steps = (8, 12, 19)" + ] + }, + { + "cell_type": "markdown", + "id": "99c7ad3c-7a5a-4dfe-81a5-9457d68fbd9c", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Computing $x$ analytically\n", + "\n", + "First, we have to construct our LSE, $Ax=b$. Once again, we reuse the settings from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "62d48e9c-8b00-43ac-9ee6-90f6418cfb05", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from qiskit_addon_mpf.static import setup_static_lse\n", + "\n", + "lse = setup_static_lse(trotter_steps, order=2, symmetric=True)" + ] + }, + { + "cell_type": "markdown", + "id": "9b5c4989-a75d-40e2-bb8a-9070188baef4", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Next, we compute the analytical solution of $x$ and its L1-norm as our reference values." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e279b0a3-9639-4e68-8138-90ff5ae6640d", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0.17239057 -1.19447005 2.02207947]\n" + ] + } + ], + "source": [ + "coeff_analytical = lse.solve()\n", + "print(coeff_analytical)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a668e3bc-0cd1-4995-bc81-9ac8ca053f02", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.388940092165899\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(np.linalg.norm(coeff_analytical, ord=1))" + ] + }, + { + "cell_type": "markdown", + "id": "abda0fe8-c188-49e5-b121-e7d9b64b5313", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Setting up the approximate model\n", + "\n", + "Now, we will construct the approximate model.\n", + "The idea of using approximate solutions $\\tilde{x}$ in order to ensure a smaller L1-norm was proposed by [Zhuk et al., 2023].\n", + "The model is already explained in detail in the API documentation of [setup_sum_of_squares_problem](/docs/api/qiskit-addon-mpf/costs#setup_sum_of_squares_problem) so we refrain from repeating it here.\n", + "Suffice to say, that the model consists of two constraints which ensure that the coefficients, $\\tilde{x}$, sum to $1$ and it enforces their L1-norm to be smaller than the user-provided `max_l1_norm` value.\n", + "The optimization objective is to minimize the deviation of $A\\tilde{x}=b$.\n", + "\n", + "We can see all of this in the output below.\n", + "Here, we are setting `max_l1_norm=3` because we already know that the L1-norm of the exact solution is approximately $3.4$ and we want to find a $\\tilde{x}$ that has a smaller L1-norm.\n", + "\n", + "[Zhuk et al., 2023]: https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.033309" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "de60da28-ce4f-427d-b8c6-11a91cae3472", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "minimize quad_over_lin(Vstack([1. 1. 1.] @ x + -1.0, [0.015625 0.00694444 0.00277008] @ x + -0.0, [2.44140625e-04 4.82253086e-05 7.67336039e-06] @ x + -0.0), 1.0)\n", + "subject to Sum(x, None, False) == 1.0\n", + " norm1(x) <= 3.0\n" + ] + } + ], + "source": [ + "from qiskit_addon_mpf.costs import setup_sum_of_squares_problem\n", + "\n", + "model, coeff_var = setup_sum_of_squares_problem(lse, max_l1_norm=3)\n", + "print(model)" + ] + }, + { + "cell_type": "markdown", + "id": "fc5da15f-7771-446b-b0cf-c9320836102a", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Solving the model with default settings\n", + "\n", + "First, let us see what happens when we solve the `model` with only default settings." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "153755d2-4c04-410c-a251-b939f460643a", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===============================================================================\n", + " CVXPY \n", + " v1.5.3 \n", + "===============================================================================\n", + "(CVXPY) Sep 05 09:28:06 AM: Your problem has 3 variables, 2 constraints, and 0 parameters.\n", + "(CVXPY) Sep 05 09:28:06 AM: It is compliant with the following grammars: DCP, DQCP\n", + "(CVXPY) Sep 05 09:28:06 AM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)\n", + "(CVXPY) Sep 05 09:28:06 AM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.\n", + "(CVXPY) Sep 05 09:28:06 AM: Your problem is compiled with the CPP canonicalization backend.\n", + "-------------------------------------------------------------------------------\n", + " Compilation \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:06 AM: Compiling problem (target solver=OSQP).\n", + "(CVXPY) Sep 05 09:28:06 AM: Reduction chain: CvxAttr2Constr -> Qp2SymbolicQp -> QpMatrixStuffing -> OSQP\n", + "(CVXPY) Sep 05 09:28:06 AM: Applying reduction CvxAttr2Constr\n", + "(CVXPY) Sep 05 09:28:06 AM: Applying reduction Qp2SymbolicQp\n", + "(CVXPY) Sep 05 09:28:06 AM: Applying reduction QpMatrixStuffing\n", + "(CVXPY) Sep 05 09:28:06 AM: Applying reduction OSQP\n", + "(CVXPY) Sep 05 09:28:06 AM: Finished problem compilation (took 1.053e-02 seconds).\n", + "-------------------------------------------------------------------------------\n", + " Numerical solver \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:06 AM: Invoking solver OSQP to obtain a solution.\n", + "-----------------------------------------------------------------\n", + " OSQP v0.6.3 - Operator Splitting QP Solver\n", + " (c) Bartolomeo Stellato, Goran Banjac\n", + " University of Oxford - Stanford University 2021\n", + "-----------------------------------------------------------------\n", + "problem: variables n = 9, constraints m = 11\n", + " nnz(P) + nnz(A) = 33\n", + "settings: linear system solver = qdldl,\n", + " eps_abs = 1.0e-05, eps_rel = 1.0e-05,\n", + " eps_prim_inf = 1.0e-04, eps_dual_inf = 1.0e-04,\n", + " rho = 1.00e-01 (adaptive),\n", + " sigma = 1.00e-06, alpha = 1.60, max_iter = 10000\n", + " check_termination: on (interval 25),\n", + " scaling: on, scaled_termination: off\n", + " warm start: on, polish: on, time_limit: off\n", + "\n", + "iter objective pri res dua res rho time\n", + " 1 0.0000e+00 1.00e+00 2.00e+02 1.00e-01 6.26e-05s\n", + " 200 4.0661e-09 7.52e-06 1.44e-08 1.22e-05 1.28e-04s\n", + "\n", + "status: solved\n", + "solution polish: unsuccessful\n", + "number of iterations: 200\n", + "optimal objective: 0.0000\n", + "run time: 1.53e-04s\n", + "optimal rho estimate: 2.09e-06\n", + "\n", + "-------------------------------------------------------------------------------\n", + " Summary \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Problem status: optimal\n", + "(CVXPY) Sep 05 09:28:07 AM: Optimal value: 4.092e-09\n", + "(CVXPY) Sep 05 09:28:07 AM: Compilation took 1.053e-02 seconds\n", + "(CVXPY) Sep 05 09:28:07 AM: Solver (including time spent in interface) took 1.028e-03 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "4.092215822096244e-09" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.solve(verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "0aafe4c0-c2cc-4de2-824f-ea3594960ca3", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We can see from the final value of the cost function that the sum of squares of $A\\tilde{x}-b$ is almost zero.\n", + "\n", + "In the output above, we also get a lot more information from [cvxpy.Problem.solve](https://www.cvxpy.org/api_reference/cvxpy.problems.html#cvxpy.Problem.solve), some of which we will get back to later.\n", + "\n", + "For now, let us inspect the final values of $\\tilde{x}$ and its L1-norm:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "662b35c2-cd7a-42ff-96a5-792184723f7c", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-0.39646076 0.55556835 0.84089365]\n", + "1.7929227539822414\n" + ] + } + ], + "source": [ + "coeff_approx_plain = coeff_var.value\n", + "print(coeff_approx_plain)\n", + "print(np.linalg.norm(coeff_approx_plain, ord=1))" + ] + }, + { + "cell_type": "markdown", + "id": "aed22d40-140e-4a06-9f6d-90be062fd0cb", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We can clearly see that the L1-norm has decreased compared to the analytical solution that we have computed before.\n", + "\n", + "Nonetheless, let us see if we can tweak our solution a bit.\n", + "In the end, we will compare the results obtained for this set of coefficients with all the other ones." + ] + }, + { + "cell_type": "markdown", + "id": "aa3484a3-ce6e-460a-a144-2bc94ab0ace4", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Tweaking the approximate model\n", + "\n", + "Rather than tweaking the input parameters like the chosen `trotter_steps` and `max_l1_norm` values, we will focus on the solver settings in this guide." + ] + }, + { + "cell_type": "markdown", + "id": "a078b0b7-93d0-418f-aaf7-f8584d6889db", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Using a different solver\n", + "\n", + "Notice, that we used the default solver method: `OSQP`. We can change that solver to see whether this has any effect." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "318369c1-7c54-4035-87b9-1e729aa0c048", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===============================================================================\n", + " CVXPY \n", + " v1.5.3 \n", + "===============================================================================\n", + "(CVXPY) Sep 05 09:28:07 AM: Your problem has 3 variables, 2 constraints, and 0 parameters.\n", + "(CVXPY) Sep 05 09:28:07 AM: It is compliant with the following grammars: DCP, DQCP\n", + "(CVXPY) Sep 05 09:28:07 AM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)\n", + "(CVXPY) Sep 05 09:28:07 AM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.\n", + "(CVXPY) Sep 05 09:28:07 AM: Your problem is compiled with the CPP canonicalization backend.\n", + "-------------------------------------------------------------------------------\n", + " Compilation \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Compiling problem (target solver=CLARABEL).\n", + "(CVXPY) Sep 05 09:28:07 AM: Reduction chain: Dcp2Cone -> CvxAttr2Constr -> ConeMatrixStuffing -> CLARABEL\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction Dcp2Cone\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction CvxAttr2Constr\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction ConeMatrixStuffing\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction CLARABEL\n", + "(CVXPY) Sep 05 09:28:07 AM: Finished problem compilation (took 8.657e-03 seconds).\n", + "-------------------------------------------------------------------------------\n", + " Numerical solver \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Invoking solver CLARABEL to obtain a solution.\n", + "-------------------------------------------------------------\n", + " Clarabel.rs v0.8.1 - Clever Acronym \n", + "\n", + " (c) Paul Goulart \n", + " University of Oxford, 2022 \n", + "-------------------------------------------------------------\n", + "\n", + "problem:\n", + " variables = 9\n", + " constraints = 11\n", + " nnz(P) = 3\n", + " nnz(A) = 30\n", + " cones (total) = 2\n", + " : Zero = 1, numel = 4\n", + " : Nonnegative = 1, numel = 7\n", + "\n", + "settings:\n", + " linear algebra: direct / qdldl, precision: 64 bit\n", + " max iter = 200, time limit = Inf, max step = 0.990\n", + " tol_feas = 1.0e-8, tol_gap_abs = 1.0e-8, tol_gap_rel = 1.0e-8,\n", + " static reg : on, ϵ1 = 1.0e-8, ϵ2 = 4.9e-32\n", + " dynamic reg: on, ϵ = 1.0e-13, δ = 2.0e-7\n", + " iter refine: on, reltol = 1.0e-13, abstol = 1.0e-12,\n", + " max iter = 10, stop ratio = 5.0\n", + " equilibrate: on, min_scale = 1.0e-4, max_scale = 1.0e4\n", + " max iter = 10\n", + "\n", + "iter pcost dcost gap pres dres k/t μ step \n", + "---------------------------------------------------------------------------------------------\n", + " 0 +7.1341e-05 -2.3333e+00 2.33e+00 2.40e-01 7.10e-01 1.00e+00 1.88e+00 ------ \n", + " 1 +7.1330e-05 -1.5432e-01 1.54e-01 1.64e-02 1.12e-01 3.28e-02 1.80e-01 9.61e-01 \n", + " 2 +6.9440e-05 -1.7266e-03 1.80e-03 2.10e-04 1.89e-03 5.65e-04 2.43e-03 9.87e-01 \n", + " 3 +2.2053e-05 -5.0002e-05 7.21e-05 5.56e-06 4.83e-05 1.98e-05 7.80e-05 9.87e-01 \n", + " 4 +1.1119e-06 -2.5543e-05 2.67e-05 1.80e-07 1.41e-06 3.21e-06 1.04e-05 9.90e-01 \n", + " 5 +7.9250e-09 -2.0484e-06 2.06e-06 2.06e-09 1.59e-08 2.11e-07 6.43e-07 9.90e-01 \n", + " 6 +2.2543e-09 -2.8390e-08 3.06e-08 2.07e-11 1.59e-10 3.12e-09 9.42e-09 9.90e-01 \n", + " 7 +1.8480e-09 -8.5733e-10 2.71e-09 1.79e-12 2.36e-10 3.19e-10 9.40e-10 9.15e-01 \n", + "---------------------------------------------------------------------------------------------\n", + "Terminated with status = Solved\n", + "solve time = 113.289µs\n", + "-------------------------------------------------------------------------------\n", + " Summary \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Problem status: optimal\n", + "(CVXPY) Sep 05 09:28:07 AM: Optimal value: 1.848e-09\n", + "(CVXPY) Sep 05 09:28:07 AM: Compilation took 8.657e-03 seconds\n", + "(CVXPY) Sep 05 09:28:07 AM: Solver (including time spent in interface) took 8.504e-04 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "1.8479854472132682e-09" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.solve(verbose=True, solver=\"CLARABEL\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "207a1f1d-00e1-40ef-bc2a-f904a58fd1ba", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-0.21284352 -0.00792683 1.22077035]\n", + "1.4415406931636157\n" + ] + } + ], + "source": [ + "coeff_approx_clarabel = coeff_var.value\n", + "print(coeff_approx_clarabel)\n", + "print(np.linalg.norm(coeff_approx_clarabel, ord=1))" + ] + }, + { + "cell_type": "markdown", + "id": "05b672be-9f72-4a11-a9ee-0d4701a8fc86", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Indeed, the outcome has changed. But now we have one major contribution from the largest $k_j$ value which is also not ideal.\n", + "\n", + "An exhaustive search of the various available solvers is beyond the scope of this guide. Instead we encourage you to try this yourself." + ] + }, + { + "cell_type": "markdown", + "id": "77ba957c-f083-4975-b793-5a96d77a8a37", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Changing the convergence thresholds\n", + "\n", + "Rather than changing the solver altogether, we can also tweak the convergence parameters.\n", + "For the `OSQP` solver the relevant ones are called `eps_abs` and `eps_rel` and they are both set to `1e-5` by default." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "74528a7b-2bba-445d-b324-a61a261d07c3", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===============================================================================\n", + " CVXPY \n", + " v1.5.3 \n", + "===============================================================================\n", + "(CVXPY) Sep 05 09:28:07 AM: Your problem has 3 variables, 2 constraints, and 0 parameters.\n", + "(CVXPY) Sep 05 09:28:07 AM: It is compliant with the following grammars: DCP, DQCP\n", + "(CVXPY) Sep 05 09:28:07 AM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)\n", + "(CVXPY) Sep 05 09:28:07 AM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.\n", + "(CVXPY) Sep 05 09:28:07 AM: Your problem is compiled with the CPP canonicalization backend.\n", + "-------------------------------------------------------------------------------\n", + " Compilation \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Compiling problem (target solver=OSQP).\n", + "(CVXPY) Sep 05 09:28:07 AM: Reduction chain: CvxAttr2Constr -> Qp2SymbolicQp -> QpMatrixStuffing -> OSQP\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction CvxAttr2Constr\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction Qp2SymbolicQp\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction QpMatrixStuffing\n", + "(CVXPY) Sep 05 09:28:07 AM: Applying reduction OSQP\n", + "(CVXPY) Sep 05 09:28:07 AM: Finished problem compilation (took 8.835e-03 seconds).\n", + "-------------------------------------------------------------------------------\n", + " Numerical solver \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Invoking solver OSQP to obtain a solution.\n", + "-----------------------------------------------------------------\n", + " OSQP v0.6.3 - Operator Splitting QP Solver\n", + " (c) Bartolomeo Stellato, Goran Banjac\n", + " University of Oxford - Stanford University 2021\n", + "-----------------------------------------------------------------\n", + "problem: variables n = 9, constraints m = 11\n", + " nnz(P) + nnz(A) = 33\n", + "settings: linear system solver = qdldl,\n", + " eps_abs = 1.0e-08, eps_rel = 1.0e-08,\n", + " eps_prim_inf = 1.0e-04, eps_dual_inf = 1.0e-04,\n", + " rho = 1.00e-01 (adaptive),\n", + " sigma = 1.00e-06, alpha = 1.60, max_iter = 10000\n", + " check_termination: on (interval 25),\n", + " scaling: on, scaled_termination: off\n", + " warm start: on, polish: on, time_limit: off\n", + "\n", + "iter objective pri res dua res rho time\n", + " 1 0.0000e+00 1.00e+00 2.00e+02 1.00e-01 5.02e-05s\n", + " 200 4.0661e-09 7.52e-06 1.44e-08 1.22e-05 2.10e-04s\n", + " 400 3.8209e-09 4.95e-06 2.88e-09 2.09e-06 3.72e-04s\n", + " 600 3.3607e-09 6.09e-06 1.96e-09 2.09e-06 4.33e-04s\n", + " 800 2.8975e-09 6.24e-06 1.84e-09 2.09e-06 4.87e-04s\n", + "1000 2.4704e-09 6.08e-06 1.70e-09 2.09e-06 5.40e-04s\n", + "1200 2.0921e-09 5.78e-06 1.56e-09 2.09e-06 5.92e-04s\n", + "1400 1.7645e-09 5.40e-06 1.44e-09 2.09e-06 6.44e-04s\n", + "1600 1.4845e-09 5.01e-06 1.32e-09 2.09e-06 7.01e-04s\n", + "1800 1.2471e-09 4.63e-06 1.21e-09 2.09e-06 7.53e-04s\n", + "2000 1.0467e-09 4.26e-06 1.11e-09 2.09e-06 8.05e-04s\n", + "2200 8.7801e-10 3.91e-06 1.01e-09 2.09e-06 8.57e-04s\n", + "2400 7.3890e-10 3.25e-06 7.74e-10 2.09e-06 9.10e-04s\n", + "2600 6.4003e-10 2.47e-06 7.11e-10 2.09e-06 9.62e-04s\n", + "2800 5.6692e-10 2.03e-06 6.68e-10 2.09e-06 1.01e-03s\n", + "3000 5.0872e-10 1.76e-06 6.33e-10 2.09e-06 1.07e-03s\n", + "3200 4.5991e-10 1.58e-06 6.02e-10 2.09e-06 1.12e-03s\n", + "3400 4.1754e-10 1.46e-06 5.74e-10 2.09e-06 1.17e-03s\n", + "3600 3.7999e-10 1.37e-06 5.47e-10 2.09e-06 1.22e-03s\n", + "3800 3.4628e-10 1.29e-06 5.22e-10 2.09e-06 1.32e-03s\n", + "4000 3.1580e-10 1.22e-06 4.99e-10 2.09e-06 1.37e-03s\n", + "4200 2.8813e-10 1.16e-06 4.76e-10 2.09e-06 1.43e-03s\n", + "4400 2.6295e-10 1.11e-06 4.55e-10 2.09e-06 1.48e-03s\n", + "4600 2.4000e-10 1.06e-06 4.35e-10 2.09e-06 1.53e-03s\n", + "4800 2.1907e-10 1.01e-06 4.15e-10 2.09e-06 1.58e-03s\n", + "5000 1.9997e-10 9.67e-07 3.97e-10 2.09e-06 1.63e-03s\n", + "5200 1.8255e-10 9.23e-07 3.79e-10 2.09e-06 1.69e-03s\n", + "5400 1.6664e-10 8.82e-07 3.62e-10 2.09e-06 1.74e-03s\n", + "5600 1.5219e-10 1.01e-06 2.60e-10 2.09e-06 1.79e-03s\n", + "5800 1.4049e-10 6.59e-07 2.47e-10 2.09e-06 1.84e-03s\n", + "6000 1.3096e-10 5.75e-07 2.39e-10 2.09e-06 1.90e-03s\n", + "6200 1.2274e-10 5.24e-07 2.31e-10 2.09e-06 1.95e-03s\n", + "6400 1.1538e-10 4.90e-07 2.24e-10 2.09e-06 2.00e-03s\n", + "6600 1.0863e-10 4.67e-07 2.17e-10 2.09e-06 2.05e-03s\n", + "6800 1.0237e-10 4.48e-07 2.11e-10 2.09e-06 2.10e-03s\n", + "7000 9.6519e-11 4.32e-07 2.05e-10 2.09e-06 2.16e-03s\n", + "7200 9.1024e-11 4.19e-07 1.99e-10 2.09e-06 2.21e-03s\n", + "7400 8.5854e-11 4.06e-07 1.93e-10 2.09e-06 2.29e-03s\n", + "7600 8.0984e-11 3.94e-07 1.88e-10 2.09e-06 2.36e-03s\n", + "7800 7.6393e-11 3.82e-07 1.82e-10 2.09e-06 2.41e-03s\n", + "8000 7.2065e-11 3.71e-07 1.77e-10 2.09e-06 2.46e-03s\n", + "8200 6.7982e-11 3.60e-07 1.72e-10 2.09e-06 2.51e-03s\n", + "8400 6.4131e-11 3.50e-07 1.67e-10 2.09e-06 2.56e-03s\n", + "8600 6.0499e-11 3.40e-07 1.62e-10 2.09e-06 2.62e-03s\n", + "8800 5.7072e-11 3.30e-07 1.57e-10 2.09e-06 2.67e-03s\n", + "9000 5.3859e-11 8.77e-06 8.55e-12 2.09e-06 2.72e-03s\n", + "9200 5.1785e-11 5.78e-07 2.18e-13 2.09e-06 2.79e-03s\n", + "9400 5.0736e-11 7.72e-08 8.32e-14 2.09e-06 2.87e-03s\n", + "9550 5.0303e-11 4.69e-08 8.73e-14 2.09e-06 2.96e-03s\n", + "plsh 4.9640e-11 1.94e-14 1.50e-12 -------- 3.00e-03s\n", + "\n", + "status: solved\n", + "solution polish: successful\n", + "number of iterations: 9550\n", + "optimal objective: 0.0000\n", + "run time: 3.00e-03s\n", + "optimal rho estimate: 2.96e-06\n", + "\n", + "-------------------------------------------------------------------------------\n", + " Summary \n", + "-------------------------------------------------------------------------------\n", + "(CVXPY) Sep 05 09:28:07 AM: Problem status: optimal\n", + "(CVXPY) Sep 05 09:28:07 AM: Optimal value: 4.964e-11\n", + "(CVXPY) Sep 05 09:28:07 AM: Compilation took 8.835e-03 seconds\n", + "(CVXPY) Sep 05 09:28:07 AM: Solver (including time spent in interface) took 3.988e-03 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "4.9640211694779584e-11" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.solve(verbose=True, eps_abs=1e-8, eps_rel=1e-8)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a5c9fe95-1752-4717-bb74-30766be22f98", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0.10925065 -1. 1.89074935]\n", + "3.0\n" + ] + } + ], + "source": [ + "coeff_approx_tight = coeff_var.value\n", + "print(coeff_approx_tight)\n", + "print(np.linalg.norm(coeff_approx_tight, ord=1))" + ] + }, + { + "cell_type": "markdown", + "id": "c841fd73-87fa-49d5-b1a8-f0de4b4c53ff", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "As you can see, we have now obtained a comparatively higher L1-norm (which is still constrained by our choice of `max_l1_norm=3`). While the coefficient of the smallest $k_j$ value is still fairly small in magnitude, at least the other two provide a reasonable amount of mixing." + ] + }, + { + "cell_type": "markdown", + "id": "7f852761-5ef5-4cc6-ab62-d7660f41e82a", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## Comparing our results\n", + "\n", + "We will now briefly repeat the computations from the [Getting started](https://qiskit.github.io/qiskit-addon-mpf/tutorials/01_getting_started.html) tutorial in order to assess the quality of the various coefficients." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "71e34633-b044-4fcc-aa63-cebc155a848f", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from qiskit.synthesis import SuzukiTrotter\n", + "from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit\n", + "\n", + "time = 8.0\n", + "circuits = []\n", + "for k in trotter_steps:\n", + " circ = generate_time_evolution_circuit(\n", + " hamiltonian,\n", + " synthesis=SuzukiTrotter(order=2, reps=k),\n", + " time=time,\n", + " )\n", + " circuits.append(circ)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "acfc17f7-9817-4576-9709-1f73aa26cf05", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['IIIIIIIIIZ', 'IIIIIIIIZI', 'IIIIIIIZII', 'IIIIIIZIII', 'IIIIIZIIII', 'IIIIZIIIII', 'IIIZIIIIII', 'IIZIIIIIII', 'IZIIIIIIII', 'ZIIIIIIIII'],\n", + " coeffs=[0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j, 0.05+0.j,\n", + " 0.05+0.j, 0.05+0.j, 0.05+0.j])\n" + ] + } + ], + "source": [ + "from qiskit.quantum_info import SparsePauliOp\n", + "\n", + "L = 10\n", + "observable = SparsePauliOp.from_sparse_list([(\"Z\", [i], 1 / L / 2) for i in range(L)], num_qubits=L)\n", + "print(observable)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "0ea7cac7-0af8-4909-ad23-7b1acb16b607", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from qiskit.primitives import StatevectorEstimator\n", + "\n", + "estimator = StatevectorEstimator()\n", + "job = estimator.run([(circ, observable) for circ in circuits])\n", + "result = job.result()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "2c2b6c4c-6a27-4601-846a-3e6757ad5156", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[array(0.23799162), array(0.35754312), array(0.38649906)]\n" + ] + } + ], + "source": [ + "evs = [res.data.evs for res in result]\n", + "print(evs)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "655f4663-d19e-4617-89c3-f0355563d915", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from scipy.linalg import expm\n", + "\n", + "exp_H = expm(-1j * time * hamiltonian.to_matrix())\n", + "\n", + "initial_state = np.zeros(exp_H.shape[0])\n", + "initial_state[0] = 1.0\n", + "\n", + "time_evolved_state = exp_H @ initial_state\n", + "\n", + "exact_obs = time_evolved_state.conj() @ observable.to_matrix() @ time_evolved_state" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "35bb0451-091b-4ed2-8776-a1aa7ccb2760", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Analytical coeff solution: 0.39548478559794153\n", + "Plain approx. solution: 0.42928990901998165\n", + "CLARABEL approx. solution: 0.41833743748333746\n", + "Tight approx. solution: 0.3992304700751579\n", + "Exact solution: 0.40060242487900255\n" + ] + } + ], + "source": [ + "print(\"Analytical coeff solution:\", evs @ coeff_analytical)\n", + "print(\"Plain approx. solution:\", evs @ coeff_approx_plain)\n", + "print(\"CLARABEL approx. solution:\", evs @ coeff_approx_clarabel)\n", + "print(\"Tight approx. solution:\", evs @ coeff_approx_tight)\n", + "print(\"Exact solution:\", exact_obs.real)" + ] + }, + { + "cell_type": "markdown", + "id": "adaea843-98fb-48ed-ad1a-0f6ad57f9a7b", + "metadata": { + "editable": true, + "raw_mimetype": "", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "We can clearly see now, that the various approximate solutions can yield quite different results.\n", + "Luckily, testing out different expansion coefficients for a fixed choice of $k_j$ values does not require us to rerun our quantum experiments.\n", + "Therefore, we encourage you to play around and tweak your expansion coefficients when using the approximate solutions." + ] + } + ], + "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-mpf/index.mdx b/docs/addons/qiskit-addon-mpf/index.mdx new file mode 100644 index 00000000000..444796ec38f --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/index.mdx @@ -0,0 +1,64 @@ +--- +title: "Qiskit addon: multi-product formulas (MPF)" +description: "Documentation for the latest version of Multi-product formulas (MPF)" +--- + +# Qiskit addon: multi-product formulas (MPF) + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package contains the Qiskit addon for multi-product formulas (MPFs). These can be used to reduce the Trotter error of Hamiltonian dynamics. + +This package currently contains the following main entry points for users: + +* `qiskit_addon_mpf.static` for working with static MPFs \[[1-2](#references)]. +* `qiskit_addon_mpf.dynamic` for working with dynamic MPFs \[[2-3](#references)]. + +## Documentation + +All documentation is available [here](https://qiskit.github.io/qiskit-addon-mpf/). + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-mpf' +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +### Optional dependencies + +The `qiskit-addon-mpf` package has a number of optional dependencies which enable certain features. The dynamic MPF feature (see \[[2-3](#references)]) is one such example. You can install the related optional dependencies like so: + +```bash +pip install 'qiskit-addon-mpf[dynamic]' +``` + + + The optional dependency [TeNPy](https://github.com/tenpy/tenpy) was previously offered under a GPLv3 license. As of the release of [v1.0.4](https://github.com/tenpy/tenpy/releases/tag/v1.0.4) on October 2nd, 2024, it has been offered under the Apache v2 license. The license of this package is only compatible with Apache-licensed versions of TeNPy. + + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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-mpf/release-notes). + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-mpf). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-mpf/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-mpf/issues/new/choose) for tracking requests and bugs. + +## References + +1. 1. Carrera Vazquez, D. J. Egger, D. Ochsner, and S. Wörner, [Well-conditioned multi-product formulas for hardware-friendly Hamiltonian simulation](https://quantum-journal.org/papers/q-2023-07-25-1067/), Quantum 7, 1067 (2023). +2. 19. Zhuk, N. Robertson, and S. Bravyi, [Trotter error bounds and dynamic multi-product formulas for Hamiltonian simulation](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.6.033309), Phys. Rev. Research 6, 033309 (2024). +3. 14. Robertson, et al. [Tensor Network enhanced Dynamic Multiproduct Formulas](https://arxiv.org/abs/2407.17405v2), arXiv:2407.17405v2 \[quant-ph]. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-mpf/blob/main/LICENSE.txt) + diff --git a/docs/addons/qiskit-addon-mpf/install.mdx b/docs/addons/qiskit-addon-mpf/install.mdx new file mode 100644 index 00000000000..fb080188c40 --- /dev/null +++ b/docs/addons/qiskit-addon-mpf/install.mdx @@ -0,0 +1,77 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Multi-product formulas (MPF)" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is 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) + +## Pre-Installation + +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-mpf` package is via `PyPI`. + +```sh +pip install 'qiskit-addon-mpf' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-addon-mpf` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-mpf.git +``` + +Next, upgrade `pip` and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-mpf +``` + +The next step is to install `qiskit-addon-mpf` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-mthree/_toc.json b/docs/addons/qiskit-addon-mthree/_toc.json new file mode 100644 index 00000000000..d3607938eda --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/_toc.json @@ -0,0 +1,176 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Matrix-free Measurement Mitigation (M3) 3.0", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "mthree (3.0.0)", + "url": "/docs/addons/qiskit-addon-mthree" + }, + { + "title": "Installation", + "url": "/docs/addons/qiskit-addon-mthree/installation" + }, + { + "title": "Citing", + "url": "/docs/addons/qiskit-addon-mthree/citing" + }, + { + "title": "Papers using M3", + "url": "/docs/addons/qiskit-addon-mthree/papers" + }, + { + "title": "Basic usage", + "url": "/docs/addons/qiskit-addon-mthree/basic" + }, + { + "title": "Using Runtime modes", + "url": "/docs/addons/qiskit-addon-mthree/runtime" + }, + { + "title": "Transpiled circuits", + "url": "/docs/addons/qiskit-addon-mthree/transpiled" + }, + { + "title": "Expectation values", + "url": "/docs/addons/qiskit-addon-mthree/expvals" + }, + { + "title": "Sampling problems", + "url": "/docs/addons/qiskit-addon-mthree/sampling" + }, + { + "title": "Using collections", + "url": "/docs/addons/qiskit-addon-mthree/collections" + }, + { + "title": "Grouped operators", + "url": "/docs/addons/qiskit-addon-mthree/grouped" + }, + { + "title": "Error analysis", + "url": "/docs/addons/qiskit-addon-mthree/error" + }, + { + "title": "Low-weight operators", + "url": "/docs/addons/qiskit-addon-mthree/marginals" + }, + { + "title": "Obtaining probabilities", + "url": "/docs/addons/qiskit-addon-mthree/probs" + }, + { + "title": "Saving and loading calibrations", + "url": "/docs/addons/qiskit-addon-mthree/cal-io" + }, + { + "title": "Utility functions", + "url": "/docs/addons/qiskit-addon-mthree/utils" + }, + { + "title": "Balanced calibration", + "url": "/docs/addons/qiskit-addon-mthree/balanced" + }, + { + "title": "Advanced usage", + "url": "/docs/addons/qiskit-addon-mthree/advanced" + }, + { + "title": "M3 paper example", + "url": "/docs/addons/qiskit-addon-mthree/tutorials/01-m-3-ex-1" + }, + { + "title": "Correcting probabilities", + "url": "/docs/addons/qiskit-addon-mthree/tutorials/02-correcting-probs" + }, + { + "title": "Quantum Volume", + "url": "/docs/addons/qiskit-addon-mthree/tutorials/03-qv-example" + }, + { + "title": "Dynamic Bernstein–Vazirani", + "url": "/docs/addons/qiskit-addon-mthree/tutorials/04-dynamic-bv" + }, + { + "title": "Basic mitigated VQE", + "url": "/docs/addons/qiskit-addon-mthree/tutorials/10-basic-vqe" + }, + { + "title": "Mitigation class", + "children": [ + { + "title": "mthree.M3Mitigation", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-m-3-mitigation" + } + ] + }, + { + "title": "Distributions", + "children": [ + { + "title": "mthree.classes.QuasiDistribution", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-classes-quasi-distribution" + }, + { + "title": "mthree.classes.ProbDistribution", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-classes-prob-distribution" + } + ] + }, + { + "title": "Distribution collections", + "children": [ + { + "title": "mthree.classes.QuasiCollection", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-classes-quasi-collection" + }, + { + "title": "mthree.classes.ProbCollection", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-classes-prob-collection" + } + ] + }, + { + "title": "Utility functions", + "children": [ + { + "title": "mthree.utils.final_measurement_mapping", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-utils-final-measurement-mapping" + }, + { + "title": "mthree.utils.expval", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-utils-expval" + }, + { + "title": "mthree.utils.stddev", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-utils-stddev" + }, + { + "title": "mthree.utils.expval_and_stddev", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-utils-expval-and-stddev" + }, + { + "title": "mthree.utils.marginal_distribution", + "url": "/docs/addons/qiskit-addon-mthree/stubs/mthree-utils-marginal-distribution" + } + ] + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Matrix-free Measurement Mitigation (M3) API reference", + "url": "/docs/api/qiskit-addon-mthree" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-mthree/advanced.mdx b/docs/addons/qiskit-addon-mthree/advanced.mdx new file mode 100644 index 00000000000..0004be67548 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/advanced.mdx @@ -0,0 +1,203 @@ +--- +title: "Advanced usage" +description: "Advanced usage for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Advanced usage + +## Options for M3Mitigation class + + + +### `iter_threshold` + +The main [`mthree.M3Mitigation`](/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation#mthree.M3Mitigation "mthree.M3Mitigation") class accepts the iter\_threshold keyword argument that determines when the automated method selector picks the iterative method over direct LU factorization (selection also depends on free memory). + +```python +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +backend = FakeCasablancaV2() +mit = mthree.M3Mitigation(backend, iter_threshold=4321) +``` + +## Options for calibration + + + +### `shots` + +When calibrating the mitigator, it is possible to vary the number of shots per calibration circuit: + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(5), shots=4321) +``` + +``` +[] +``` + + + +### `method` + +There are three ways to do the calibration. The default `balanced` method a set of circuits with varying bitstring patterns such that the $|0\rangle$ and $|1\rangle$ states are each prepared an even number of times, as is their pair-wise correlations. For example, the balanced bit-strings over four qubits are: + +```python +gen = mthree.generators.HadamardGenerator(4) +list(gen) +``` + +``` +[array([0, 0, 0, 0], dtype=uint8), + array([1, 0, 1, 0], dtype=uint8), + array([0, 1, 1, 0], dtype=uint8), + array([1, 1, 0, 0], dtype=uint8), + array([0, 0, 0, 1], dtype=uint8), + array([1, 0, 1, 1], dtype=uint8), + array([0, 1, 1, 1], dtype=uint8), + array([1, 1, 0, 1], dtype=uint8)] +``` + +The `independent` method also sends $2N$ circuits but measures only a single qubit at a time. As such, this is a truly uncorrelated calibration process. + +Finally, a `marginal` calibration can also be done that sends only two circuits, $|0\rangle^{\otimes N}$ and $|1\rangle^{\otimes N}$, and marginalizes over the results to get the needed $2N$ error rates. These two states are a sub-set of the `balanced` calibrations, and are what Qiskit uses in its readout mitigation. This method is very sensitive to state preparation errors, and should not be used in practice. + +An example setting the method is + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(5), method='independent') +``` + +``` +[] +``` + + + +### `rep_delay` + + + Do not set this unless you know what you are doing. + + +The `rep_delay` keyword argument sets the time between calibration circuits on IBM Quantum systems. This option exists to test and reduce the presence of state prep errors. The calibration circuits in M3 all contain conditional resets at the beginning so as to minimize the need for setting this option. + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(5), rep_delay=400e-6) +``` + +``` +[] +``` + + + +## `initial_reset` + + + Do not set this unless you know what you are doing. + + +A boolean value that specifies whether reset instructions should be used at the beginning of the calibration circuits. Ideally this helps to suppress any residual state prep errors that occur from imperfect reset of the qubits. Can be used in concert with, or as an alternative to `rep_delay`. Note that, in order for this to work, the circuits that need to be mitigated must also have reset instructions at the beginning. Otherwise you are calibrating for no state-prep errors, but the actual circuits may still suffer from these errors. + +## Options when applying corrections + +Here we first calibrate a mitigator and generate raw counts: + +```python +from qiskit import * + +qc = QuantumCircuit(6) +qc.reset(range(6)) +qc.h(3) +qc.cx(3,1) +qc.cx(3,5) +qc.cx(1,0) +qc.cx(5,4) +qc.cx(1,2) +qc.measure_all() + +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(6)) + +trans_qc = transpile(qc, backend) +raw_counts = backend.run(trans_qc, shots=8192).result().get_counts() +``` + + + +### `method` + +There are two ways to solve the linear system of equations generated by M3. First, the `direct` method uses LU-factorization by constructing the reduced assignment matrix. Second the `iterative` method uses preconditioned iterative solvers to find the solution vector without explicit matrix construction. By default M3 uses an `auto` method that selects the appropriate solution method based on the number of unique bit-strings and the available free memory on the computer. To override this, one can simply set the option: + +```python +quasis = mit.apply_correction(raw_counts, range(6), method='iterative') +``` + + + +### `distance` + +Optionally one may truncate the M3 assignment matrix to only those elements of the matrix that are transitions between elements less than or equal to a given Hamming distance away from each other. This does not change the dimensionality of the underlying matrix, but rather changes the sparsity pattern of the elements. This is done using the `distance` keyword argument. By default, M3 computes the corrections out to the full distance. In practice, including only up to `distance=3` elements yields accurate answers in most cases. + +```python +quasis = mit.apply_correction(raw_counts, range(6), distance=3) +``` + + + +### `details` + +Allows one to see additional information about the solution. This changes the return of the :method:`mthree.M3Mitigation.apply_correction` method to a tuple of two values: + +```python +quasis, details = mit.apply_correction(raw_counts, range(6), details=True) +print(details) +``` + +``` +{'method': 'direct', 'time': 0.0003400829737074673, 'dimension': 44, 'col_norms': array([0.9994606 , 0.9851231 , 0.98098224, 0.9847878 , 0.98490703, + 0.9848617 , 0.97361916, 0.99131244, 0.9914631 , 0.9924751 , + 0.969823 , 0.9843446 , 0.9990387 , 0.9703281 , 0.97626734, + 0.95813066, 0.9912847 , 0.98026615, 0.9515494 , 0.9799929 , + 0.98020184, 0.93811506, 0.978891 , 0.96814924, 0.9288414 , + 0.9763872 , 0.95401514, 0.96020985, 0.9307259 , 0.93472075, + 0.9966233 , 0.9681019 , 0.9053964 , 0.9027333 , 0.91178197, + 0.9701343 , 0.9986021 , 0.96056825, 0.9553432 , 0.9967951 , + 0.9599821 , 0.99687046, 0.9965531 , 0.9993191 ], dtype=float32)} +``` + + + +### `max_iter` + + + Do not set this unless you know what you are doing. + + +Sets the maximum number of iterations performed by the iterative solver. + +```python +quasis = mit.apply_correction(raw_counts, range(6), method='iterative', max_iter=10) +``` + + + +### `tol` + + + Do not set this unless you know what you are doing. + + +Sets the tolerance of the iterative solver. Might need adjustments to `max_iter` if value is set too low. + +```python +quasis = mit.apply_correction(raw_counts, range(6), method='iterative', tol=1e-5) +``` + diff --git a/docs/addons/qiskit-addon-mthree/balanced.mdx b/docs/addons/qiskit-addon-mthree/balanced.mdx new file mode 100644 index 00000000000..4c80ccd6680 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/balanced.mdx @@ -0,0 +1,47 @@ +--- +title: "Balanced calibrations" +description: "Balanced calibrations for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Balanced calibrations + +The default calibration method for M3 is what is called “balanced” calibration. Balanced calibrations sample all independent and pair-wise readout error rates evenly; hence the name “balanced”. In M3 v3 and higher, this routine has been updated to use a method from Bravyi et al, Phys. Rev. A 103, 042605 (2021). + +To see the bit-patterns used to generate the calibration circuits one can call the generator explicitly. E.g. to see the strings for 5-qubits one would do + +```python +import mthree + +gen = mthree.generators.HadamardGenerator(5) +list(gen) +``` + +``` +[array([0, 0, 0, 0, 0], dtype=uint8), + array([1, 0, 1, 0, 1], dtype=uint8), + array([0, 1, 1, 0, 0], dtype=uint8), + array([1, 1, 0, 0, 1], dtype=uint8), + array([0, 0, 0, 1, 1], dtype=uint8), + array([1, 0, 1, 1, 0], dtype=uint8), + array([0, 1, 1, 1, 1], dtype=uint8), + array([1, 1, 0, 1, 0], dtype=uint8)] +``` + +For every position in the bit-string you will see that 0 or 1 appear an equal number of times, and the same is true for all pair-wise combinations of 0 and 1. If there is a 0, then that circuit samples the $|0\rangle$ state for that qubit, similarly for the 1 element. So when we execute the balanced calibration circuits using shots number of samples, each error rate in the calibration data is actually being sampled more times than requested. Thus, when you pass the shots value to M3, in the balanced calibration mode internally it divides by the number of measured qubits so that the precision of each error rate matches the precision of the other methods. That is to say that the following: + +> ```python +> from qiskit_ibm_runtime.fake_provider import FakeAthensV2 +> +> backend = FakeAthensV2() +> mit = mthree.M3Mitigation(backend) +> mit.cals_from_system(method='balanced') +> ``` +> +> ``` +> [] +> ``` + +Will sample each independent qubit error rate 10000 times (or the max allowed by the target system if less) regardless of which method is used. All pair-wise correlations are measured half this number of times. This yields a calibration process whose overhead is independent of the number of qubits used; there is no additional cost to compute the calibration over a full device. Note that, when using a simulator or “fake” device, M3 defaults to independent calibration mode for efficiency. As such, to enable balanced calibration on a simulator one must explicitly set the method\` as done above. + diff --git a/docs/addons/qiskit-addon-mthree/basic.mdx b/docs/addons/qiskit-addon-mthree/basic.mdx new file mode 100644 index 00000000000..be8f2e77bc5 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/basic.mdx @@ -0,0 +1,97 @@ +--- +title: "Basic usage" +description: "Basic usage for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Basic usage + +Using M3 involves three steps (steps one and two can usually be done in reverse order if desired). + +> 1. Select a system and calibrate over the desired set of qubits. +> 2. Run the circuit(s) of interest on the system. +> 3. Apply the readout correction and post-process. + +## Simple example + +Here we use a noisy simulator to perform the three steps above. First we import the needed modules, and construct a circuit of interest. + +```python +import numpy as np +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(6) +qc.reset(range(6)) +qc.h(3) +qc.cx(3,1) +qc.cx(3,5) +qc.cx(1,0) +qc.cx(5,4) +qc.cx(1,2) +qc.measure_all() +qc.draw('mpl') +``` + +![\_images/basic\_0\_1.png](/docs/images/addons/qiskit-addon-mthree/basic_0_1.avif) + +Next we calibrate an M3 mitigator instance over qubits 0 -> 6 (Step #1): + +```python +backend = FakeCasablancaV2() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(6)) +``` + +``` +[] +``` + +Transpile and execute our circuit (Step #2): + +```python +trans_qc = transpile(qc, backend) +raw_counts = backend.run(trans_qc).result().get_counts() +``` + +Finally, apply the correction and post-process (Step #3). Here our post-processing is simply computing the expectation value from the returned quasi-probabilities: + +```python +quasis = mit.apply_correction(raw_counts, range(6)) +print('Expectation value:',quasis.expval()) +``` + +``` +Expectation value: 0.9226210117340088 +``` + +## Specifying qubits to mitigate over + +The circuit above also fits on other systems without SWAP mapping provided that we select the correct layout. + +```python +from qiskit_ibm_runtime.fake_provider import FakeMontrealV2 + +backend = FakeMontrealV2() +mit2 = mthree.M3Mitigation(backend) +``` + +In our case, `qubits = [10, 12, 15, 13, 11, 14]` is an appropriate layout. Importantly, the zeroth entry of the list tells us what physical qubit is readout to generate bit 0 in the output bit-strings. We must pass this list to both the calibration and correction steps of M3. + +```python +qubits = [10, 12, 15, 13, 11, 14] +mit2.cals_from_system(qubits) + +trans_qc = transpile(qc, backend, initial_layout=qubits) +raw_counts2 = backend.run(trans_qc).result().get_counts() + +quasis2 = mit2.apply_correction(raw_counts2, qubits) +print('Expectation value:',quasis2.expval()) +``` + +``` +Expectation value: 0.9692449569702148 +``` + diff --git a/docs/addons/qiskit-addon-mthree/cal-io.mdx b/docs/addons/qiskit-addon-mthree/cal-io.mdx new file mode 100644 index 00000000000..0063e9f9c6b --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/cal-io.mdx @@ -0,0 +1,66 @@ +--- +title: "Saving and loading calibration data" +description: "Saving and loading calibration data for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Saving and loading calibration data + +It is possible to save calibration data and, optionally, re-load it at a later time. Let us generate some calibration data and save it. + +```python +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +backend = FakeCasablancaV2() + +mit = mthree.M3Mitigation(backend) +mit.cals_from_system([1, 3, 5], cals_file='my_cals.json') +mit.single_qubit_cals +``` + +``` +[None, + array([[0.993042 , 0.02490234], + [0.00695801, 0.97509766]], dtype=float32), + None, + array([[0.984375 , 0.01989746], + [0.015625 , 0.98010254]], dtype=float32), + None, + array([[0.993042 , 0.0279541 ], + [0.00695801, 0.9720459 ]], dtype=float32), + None] +``` + +or, + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system([1, 3, 5]) +mit.cals_to_file('my_cals.json') +``` + +We can then load this data at a later point in time using: + +```python +import mthree + +mit2 = mthree.M3Mitigation() +mit2.cals_from_file('my_cals.json') +mit2.single_qubit_cals +``` + +``` +[None, + array([[0.99328613, 0.02148438], + [0.00671387, 0.9785156 ]], dtype=float32), + None, + array([[0.9842529 , 0.01550293], + [0.01574707, 0.9844971 ]], dtype=float32), + None, + array([[0.9935303 , 0.02587891], + [0.00646973, 0.9741211 ]], dtype=float32), + None] +``` + diff --git a/docs/addons/qiskit-addon-mthree/citing.mdx b/docs/addons/qiskit-addon-mthree/citing.mdx new file mode 100644 index 00000000000..586ff4f2d98 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/citing.mdx @@ -0,0 +1,13 @@ +--- +title: "Citing" +description: "Citing for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Citing + +If you find M3 useful, we would be delighted if you cite it in your work: + +> “Scalable Mitigation of Measurement Errors on Quantum Computers”, Paul D. Nation, Hwajung Kang, Neereja Sundaresan, and Jay M. Gambetta, [PRX Quantum 2, 040326 (2021)](https://doi.org/10.1103/PRXQuantum.2.040326). + diff --git a/docs/addons/qiskit-addon-mthree/collections.mdx b/docs/addons/qiskit-addon-mthree/collections.mdx new file mode 100644 index 00000000000..14532749f52 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/collections.mdx @@ -0,0 +1,71 @@ +--- +title: "Using distribution collections" +description: "Using distribution collections for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Using distribution collections + +When you mitigate over multiple circuits the return object is a [`mthree.classes.QuasiCollection`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection#mthree.classes.QuasiCollection "mthree.classes.QuasiCollection") + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(6) +qc.reset(range(6)) +qc.h(3) +qc.cx(3,1) +qc.cx(3,5) +qc.cx(1,0) +qc.cx(5,4) +qc.cx(1,2) +qc.measure_all() + +backend = FakeCasablancaV2() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(6)) + +trans_qc = transpile([qc]*10, backend) +raw_counts = backend.run(trans_qc, shots=4000).result().get_counts() + +quasis = mit.apply_correction(raw_counts, range(6), return_mitigation_overhead=True) +type(quasis) +``` + +``` +mthree.classes.QuasiCollection +``` + +`QuasiCollection` objects allow one to work with multiple distributions in the same manner as a single one. E.g. we can get the mitigation overhead of the whole collection + +```python +quasis.mitigation_overhead +``` + +``` +array([1.7319335, 1.7434431, 1.7570623, 1.7362996, 1.7422098, 1.7463065, + 1.7460448, 1.7474257, 1.7384585, 1.7354022], dtype=float32) +``` + +or compute expectation values and standard deviations over the full set: + +```python +quasis.expval_and_stddev('IZIZIZ') +``` + +``` +[(0.06401509046554565, 0.0208082523361218), + (0.006104528903961182, 0.02087727925403053), + (0.03547114133834839, 0.020958663872588774), + (0.06163862347602844, 0.020834464102203316), + (0.016530781984329224, 0.020869893662517725), + (0.03425535559654236, 0.02089441633079794), + (0.03737133741378784, 0.02089285001670664), + (0.01931297779083252, 0.020901110175608882), + (0.06001356244087219, 0.020847412652591435), + (0.04732754826545715, 0.02082907978781109)] +``` + diff --git a/docs/addons/qiskit-addon-mthree/error.mdx b/docs/addons/qiskit-addon-mthree/error.mdx new file mode 100644 index 00000000000..55c693b139a --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/error.mdx @@ -0,0 +1,85 @@ +--- +title: "Error analysis" +description: "Error analysis for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Error analysis + +Mitigating readout errors does not come for free. Instead, there is an overhead that results in increased uncertainty in the computed results. M3 will optionally compute this overhead and return an upper-bound on the expected standard deviation (variance) of the computed expectation values. + +Let us first calibrate the mitigator and get raw results: + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(6) +qc.reset(range(6)) +qc.h(3) +qc.cx(3,1) +qc.cx(3,5) +qc.cx(1,0) +qc.cx(5,4) +qc.cx(1,2) +qc.measure_all() +qc.draw('mpl') +``` + +![\_images/error\_0\_0.png](/docs/images/addons/qiskit-addon-mthree/error_0_0.avif) + +```python +backend = FakeCasablancaV2() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(6)) +``` + +``` +[] +``` + +```python +trans_qc = transpile(qc, backend) +raw_counts = backend.run(trans_qc, shots=8192).result().get_counts() +``` + +In order to compute the standard deviation of our mitigated expectation values we need to request the information be computed: + +```python +quasis = mit.apply_correction(raw_counts, range(6), return_mitigation_overhead=True) +``` + +The mitigation overhead is returned as an attribute of the returned quasi-probabilities + +```python +quasis.mitigation_overhead +``` + +``` +np.float64(1.7767365338952634) +``` + +that, together with the number of shots taken, determines the upper-bound on the standard deviation: + +```python +quasis.stddev() +``` + +``` +0.014727076557238428 +``` + +It is also possible to return both the expectation value and standard deviation in a single call: + +```python +quasis.expval_and_stddev() +``` + +``` +(0.8965940475463867, 0.014727076557238428) +``` + +Although this standard deviation is an upper-bound, it is usually a tight upper-bound that can be faithfully used further analysis. + diff --git a/docs/addons/qiskit-addon-mthree/expvals.mdx b/docs/addons/qiskit-addon-mthree/expvals.mdx new file mode 100644 index 00000000000..95eb5bd56ec --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/expvals.mdx @@ -0,0 +1,125 @@ +--- +title: "Obtaining expectation values" +description: "Obtaining expectation values for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Obtaining expectation values + +Given a quasi- or standard probability distribution, it is possible to compute the expectation value of diagonal operators directly from the distributions (or collections) of distributions. This can be done using string representation for standard diagonal operators such as `I`, `Z`, `0` or `1`, or via dictionaries for custom operators. + +Let us first generate some quasi-distributions by mitigating 2- and 3-qubit GHZ circuits on a noisy-simulator. + +```python +import numpy as np +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeAthensV2 +import mthree + +backend = FakeAthensV2() + +ghz2 = QuantumCircuit(2) +ghz2.h(0) +ghz2.cx(0,1) +ghz2.measure_all() + +trans_ghz2 = transpile(ghz2, backend) + +ghz3 = QuantumCircuit(3) +ghz3.h(0) +ghz3.cx(0,1) +ghz3.cx(1,2) +ghz3.measure_all() + +trans_ghz3 = transpile(ghz3, backend) + +raw2 = backend.run(trans_ghz2, shots=4000).result().get_counts() +raw3 = backend.run(trans_ghz3, shots=4000).result().get_counts() + +mit = mthree.M3Mitigation(backend) +mit.cals_from_system() + +quasi2 = mit.apply_correction(raw2, [0,1], return_mitigation_overhead=True) +quasi3 = mit.apply_correction(raw3, [0,1,2], return_mitigation_overhead=True) +``` + +Now let us compute the expectation values of these distributions for the default case of `Z` operators on each qubit: + +```python +print('GHZ2:', quasi2.expval()) +print('GHZ3:', quasi3.expval()) +``` + +``` +GHZ2: 0.9362177848815918 +GHZ3: 0.029018640518188477 +``` + +The values are close to one and zero, respectively. We can use strings to repeat the above via: + +```python +print('GHZ2:', quasi2.expval('ZZ')) +print('GHZ3:', quasi3.expval('ZZZ')) +``` + +``` +GHZ2: 0.9362177848815918 +GHZ3: 0.029018640518188477 +``` + +Replacing a `Z` measurement with an `I` on one of the qubits has the affect of changing the sign for the $|1>^{\otimes N}$ component: + +```python +print('GHZ2:', quasi2.expval('IZ')) +print('GHZ3:', quasi3.expval('ZIZ')) +``` + +``` +GHZ2: 0.021010488271713257 +GHZ3: 0.9692444801330566 +``` + +We can also pass lists of strings: + +```python +quasi3.expval_and_stddev(['ZZZ','ZIZ']) +``` + +``` +(array([0.02901864, 0.9692445 ], dtype=float32), 0.018216806955407207) +``` + +Alternatively, users can specify their own custom diagonal operators using dictionaries. Here we form the projectors on the all ones and zeros states: + +```python +all_zeros_proj = {'000': 1} +all_ones_proj = {'111': 1} +quasi3.expval(all_zeros_proj) +``` + +``` +0.5033974051475525 +``` + +Like strings, one can pass an array of dicts: + +```python +quasi3.expval([all_zeros_proj, all_ones_proj]) +``` + +``` +array([0.5033974 , 0.48120362], dtype=float32) +``` + +We can verify that the projectors return the correct values: + +```python +p0s, p1s = quasi3.expval([all_zeros_proj, all_ones_proj]) +np.allclose([p0s, p1s], [quasi3['000'], quasi3['111']]) +``` + +``` +True +``` + diff --git a/docs/addons/qiskit-addon-mthree/grouped.mdx b/docs/addons/qiskit-addon-mthree/grouped.mdx new file mode 100644 index 00000000000..144e6c63b08 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/grouped.mdx @@ -0,0 +1,104 @@ +--- +title: "Grouped operators" +description: "Grouped operators for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Grouped operators + +Often times when evaluating expectation values it is possible to group operators together if the Pauli operators from which they are comprised commute with each other. when possible, this batching can greatly reduce the number of executions needed. To see how to do this consider the following simple example: + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeAthensV2 +import mthree + +qc = QuantumCircuit(4) +qc.h(0) +qc.cx(0, range(1, 4)) +qc.measure_all() +qc.draw('mpl') +``` + +![\_images/grouped\_0\_0.png](/docs/images/addons/qiskit-addon-mthree/grouped_0_0.avif) + +Here we will generate and execute two circuits on the target system (fake system in this case), and, because we have transpiled, find the final measurement mapping: + +```python +backend = FakeAthensV2() +trans_circs = transpile([qc]*2, backend, optimization_level=3, approximation_degree=0) +mappings = mthree.utils.final_measurement_mapping(trans_circs) +``` + +Let us execute and get the resultant counts: + +```python +job = backend.run(trans_circs, shots=10000) +counts = job.result().get_counts() +``` + +We can now mitigate as usual: + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(mappings, shots=10000) +quasis = mit.apply_correction(counts, mappings, return_mitigation_overhead=True) +``` + +Now that we have the final mitigated quasi-distributions we can compute expectation values. For batched operators, the expectation values are grouped together based on which quasi-distribution you want to evaluate them with: + +```python +quasis.expval([['IIII', 'ZZZZ', '0000', '1111'], ['IIII']]) +``` + +``` +[array([0.9999999 , 0.907704 , 0.4837907 , 0.45387825], dtype=float32), + array([0.9999999], dtype=float32)] +``` + +The return list has the expectation values grouped into NumPy arrays. A similar procedure works for expectation values and standard deviations: + +```python +quasis.expval_and_stddev([['IIII', 'ZZZZ', '0000', '1111'], ['IIII']]) +``` + +``` +[(array([0.9999999 , 0.907704 , 0.4837907 , 0.45387825], dtype=float32), + 0.01225381672502408), + (array([0.9999999], dtype=float32), 0.012211955337045909)] +``` + +If you have a single expectation value string then you do not need to wrap it in a list: + +```python +quasis.expval([['IIII', 'ZZZZ', '0000', '1111'], 'IIII']) +``` + +``` +[array([0.9999999 , 0.907704 , 0.4837907 , 0.45387825], dtype=float32), + 0.9999998807907104] +``` + +Of course probability-distributions work the same way as quasi-distributions: + +```python +probs = quasis.nearest_probability_distribution() +probs.expval([['IIII', 'ZZZZ', '0000', '1111'], ['IIII']]) +``` + +``` +[array([1. , 0.90862715, 0.483733 , 0.45382056], dtype=float32), + array([1.], dtype=float32)] +``` + +```python +probs.expval_and_stddev([['IIII', 'ZZZZ', '0000', '1111'], ['IIII']]) +``` + +``` +[(array([1. , 0.90862715, 0.483733 , 0.45382056], dtype=float32), + 0.01225381672502408), + (array([1.], dtype=float32), 0.012211955337045909)] +``` + diff --git a/docs/addons/qiskit-addon-mthree/index.mdx b/docs/addons/qiskit-addon-mthree/index.mdx new file mode 100644 index 00000000000..5406abca4d5 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/index.mdx @@ -0,0 +1,25 @@ +--- +title: "mthree (3.0.0)" +description: "Documentation for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# mthree (3.0.0) + +mthree is a package for scalable quantum measurement error mitigation that need not explicitly form the assignment matrix, or its inverse, as is thus a **m**atrix-free **m**easurement **m**itigation (M3) routine. + +M3 works in a reduced subspace defined by the noisy input bitstrings that are to be corrected. Because the number of unique bitstrings can be much smaller than the dimensionality of the full multi-qubit Hilbert space, the resulting linear system of equations is nominally much easier to solve. + +![\_images/truncation.png](/docs/images/addons/qiskit-addon-mthree/truncation.avif) + +It is often the case that this linear equation is trivial to solve using LU decomposition, using only modest computing resources. However, if the number of unique bitstrings is large, and / or one has very tight memory constraints, then the problem can be solved in a matrix-free manner using a preconditioned iterative linear solution method, e.g. the Generalized minimal residual (GMRES) or biconjugate gradient stabilized (BiCGSTAB) methods. + +M3 is suitable for problems amenable to using quasi-probabilities such as those formulated in terms of expectation values, or sampling problems where, for example, one is interested in the bit-string with the highest probability. Quasi-probabilities can be projected onto the nearest probability distribution if true probabilities are desired, but this makes error analysis more difficult. M3 works for mid-circuit measurements as well, provided that one is interested in ensemble averages, as opposed to correcting single-shot measurements; it cannot mitigate single-shot measurements used for conditional-gate logic. + + + Not sure where to get started? + + Have a look at how others have utilized M3: [Papers using M3](papers#papers). + + diff --git a/docs/addons/qiskit-addon-mthree/installation.mdx b/docs/addons/qiskit-addon-mthree/installation.mdx new file mode 100644 index 00000000000..cf52db656da --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/installation.mdx @@ -0,0 +1,60 @@ +--- +title: "Installation" +description: "Installation for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + +# Installation + +You can pip install M3 in serial mode using PyPi via: + +```bash +pip install mthree +``` + +Alternatively, one can install from source: + +```bash +python setup.py install +``` + +To enable OpenMP one must have an OpenMP 3.0+ enabled compiler and install with: + +```bash +python setup.py install --with-openmp +``` + +Optionally one can also set `-march=native` using: + +```bash +python setup.py install --with-native +``` + +The `openmp` and `native` flags can be used simultaneously using a comma. + +## OpenMP on OSX + +On OSX one must install LLVM using homebrew (You cannot use GCC): + +```bash +brew install llvm +``` + +after which the following (or the like) must be executed in the terminal: + +```bash +export PATH="/usr/local/opt/llvm/bin:$PATH" +``` + +and + +```bash +export LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" +export CPPFLAGS="-I/usr/local/opt/llvm/include" +``` + +Then installation with openmp can be accomplished using: + +```bash +CC=clang CXX=clang python setup.py install --with-openmp +``` + diff --git a/docs/addons/qiskit-addon-mthree/marginals.mdx b/docs/addons/qiskit-addon-mthree/marginals.mdx new file mode 100644 index 00000000000..6555d2315e5 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/marginals.mdx @@ -0,0 +1,130 @@ +--- +title: "Low-weight observables (Marginals)" +description: "Low-weight observables (Marginals) for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Low-weight observables (Marginals) + +Often one is interested in computing expectation values of “low-weight” observables, where the operator is comprised of one or more identity operators, e.g. $\langle IIZZII\rangle$. Qubits with an identity do not need to be corrected because the eigenvalues associated with the $|0\rangle$ and $|1\rangle$ states are the same. Because M3 scales with the number of bit-strings, removing these unneeded elements can reduce the amount of computation required. Below is an example of how to use M3 to marginalize and correct over the smaller distribution. + +Consider the following circuit that we would like to evaluate: + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeGuadalupeV2 +import mthree + +N = 6 +qc = QuantumCircuit(N) +qc.x(range(0, N)) +qc.h(range(0, N)) +for kk in range(N//2,0,-1): + qc.ch(kk, kk-1) +for kk in range(N//2, N-1): + qc.ch(kk, kk+1) +qc.measure_all() +qc.draw('mpl') +``` + +![\_images/marginals\_0\_0.png](/docs/images/addons/qiskit-addon-mthree/marginals_0_0.avif) + +Let us first map this onto the target system and compute the final measurement mapping: + +```python +backend = FakeGuadalupeV2() + +trans_qc = transpile(qc, backend, optimization_level=3, seed_transpiler=12345) +mapping = mthree.utils.final_measurement_mapping(trans_qc) +mapping +``` + +``` +{0: 14, 1: 13, 2: 12, 3: 10, 4: 7, 5: 6} +``` + +Now assume we are only interested in many low-weight operators such as $\langle ZIIZIZ\rangle$ or $\langle ZZIIII\rangle$. How would we compute the corrected expectation values? Because there are identity operators, we can marginalize the distributions to be only over those qubits that actually play a role in the expectation value. However, we must remember to keep track of which physical qubits go to which classical bits. We can do this with [`mthree.utils.marginal_distribution()`](/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution#mthree.utils.marginal_distribution "mthree.utils.marginal_distribution"):. + +First let us first get a raw distribution: + +```python +counts = backend.run(trans_qc, shots=10000).result().get_counts() +``` + +We can get the marginal distributions in two ways. First we can directly pass a list of indices, e.g. for the operator $\langle ZIIZIZ\rangle$ the list would be \[0, 2, 5]: + +```python +mthree.utils.marginal_distribution(counts, [0, 2, 5]) +``` + +``` +{'101': 1599, + '011': 822, + '110': 309, + '111': 5986, + '100': 503, + '001': 553, + '010': 48, + '000': 180} +``` + +or we can use the operator itself: + +```python +mthree.utils.marginal_distribution(counts, 'ZIIZIZ') +``` + +``` +{'111': 5986, + '100': 503, + '010': 48, + '011': 822, + '110': 309, + '000': 180, + '001': 553, + '101': 1599} +``` + +Now we have a marginal distribution and in principle we have enough. However, having a reduced mapping of physical qubits that correspond to the bits in the marginal distribution would be handy to have. We can pass a mapping to marginal\_distribution and get such a reduced mapping: + +```python +marginal_counts, reduced_map = mthree.utils.marginal_distribution(counts, 'ZIIZIZ', mapping=mapping) +reduced_map +``` + +``` +{0: 14, 1: 12, 2: 6} +``` + +We can now mitigate in a smaller probability space: + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(reduced_map, shots=25000) + +quasi = mit.apply_correction(marginal_counts, reduced_map) +quasi +``` + +``` +{'000': np.float32(0.014724134), + '001': np.float32(0.05168699), + '010': np.float32(0.0011133504), + '011': np.float32(0.073390774), + '100': np.float32(0.044632103), + '101': np.float32(0.14970978), + '110': np.float32(0.0052394327), + '111': np.float32(0.6595034)} +``` + +Because the only non-identity operators are typically Z operators it is easy to compute the eigenvalues because the operator will be all Z’s: + +```python +quasi.expval() +``` + +``` +-0.5138717293739319 +``` + diff --git a/docs/addons/qiskit-addon-mthree/papers.mdx b/docs/addons/qiskit-addon-mthree/papers.mdx new file mode 100644 index 00000000000..5eabed83c40 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/papers.mdx @@ -0,0 +1,217 @@ +--- +title: "Papers using M3" +description: "Papers using M3 for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Papers using M3 + +1. “Achieving computational gains with quantum error correction primitives: Generation of long-range entanglement enhanced by error detection”, Haoran Liao, Gavin S. Hartnett, Ashish Kakkar, Adrian Tan, Michael Hush, Pranav S. Mundada, Michael J. Biercuk, Yuval Baum, [arXiv:2411.14638](https://doi.org/10.48550/arXiv:2411.14638). +2. “Next Generation Power System Planning and Operation With Quantum Computation”, Priyanka Arkalgud Ganeshamurthy, Kumar Ghosh, Corey O’Meara, Giorgio Cortiana, Jan Schiefelbein-Lach, Antonello Monti, [EEE Access, vol. 12, 182673, 2024](https://doi.org/10.1109/ACCESS.2024.3509743). +3. “Mitigating exponential concentration in covariant quantum kernels for subspace and real-world data”, Gabriele Agliardi, Giorgio Cortiana, Anton Dekusar, Kumar Ghosh, Naeimeh Mohseni, Corey O’Meara, Víctor Valls, Kavitha Yogaraj, Sergiy Zhuk, [arXiv:2412.07915](https://doi.org/10.48550/arXiv:2412.07915). +4. “Sparse Non-Markovian Noise Modeling of Transmon-Based Multi-Qubit Operations”, Yasuo Oda, Kevin Schultz, Leigh Norris, Omar Shehab, Gregory Quiroz, [arXiv:2412.16092](https://doi.org/10.48550/arXiv:2412.16092). +5. “Experimental computations of atomic properties on a superconducting quantum processor”,Akhil Pratap Singh, Kenji Sugisaki, Srinivasa Prasannaa, Bijaya Kumar Sahoo, Bhanu Pratap Das, and Yasunobu Nakamura, [Phys. Rev. A 110, 062620 (2024)](https://doi.org/10.1103/PhysRevA.110.062620). +6. “Enhancing computational accuracy with parallel parameter optimization in variational quantum eigensolver”, Daisuke Tsukayama, Jun-ichi Shirakashi, Tetsuo Shibuya, Hiroshi Imai, [AIP Advances 15, 015226 (2025)](https://doi.org/10.1063/5.0236028). +7. “Quantum Error Correction Without Encoding via the Circulant Structure of Pauli Noise and the Fast Fourier Transform”, Alvin Gonzales, [arXiv:2501.01953](https://doi.org/10.48550/arXiv:2501.01953). +8. “Developing techniques for Simulation of SU(3) Quantum Field Theories on State-of-the-Art Quantum Devices”, Ivan Chernyshev, [arXiv:2502.02502](https://doi.org/10.48550/arXiv:2502.02502). +9. “Certified Random Number Generation using Quantum Computers”, Pingal Pratyush Nath, Aninda Sinha, Urbasi Sinha, [arXiv:2502.02973](https://doi.org/10.48550/arXiv:2502.02973). +10. “First-principle crosstalk dynamics and Hamiltonian learning via Rabi experiments”, Jan Balewski, Adam Winick, Yilun Xu, Neel Vora, Gang Huang, David Santiago, Joseph Emerson, Irfan Siddiqi, [arXiv:2502.05362](https://doi.org/10.48550/arXiv:2502.05362). +11. “Variational Quantum Algorithms for Many-Body Systems”, Mirko Consiglio, [arXiv:2502.11985](https://doi.org/10.48550/arXiv:2502.11985). +12. “Benchmarking MedMNIST dataset on real quantum hardware”, Gurinder Singh, Hongni Jin, Kenneth M. Merz Jr, [arXiv:2502.13056](https://doi.org/10.48550/arXiv:2502.13056). +13. “Efficient Quantum Chemistry Calculations on Noisy Quantum Hardware”, Nora Bauer, Kübra Yeter-Aydeniz, George Siopsis, [arXiv:2503.02778](https://doi.org/10.48550/arXiv:2503.02778). +14. “Non-equilibrium thermodynamics of precision through a quantum-centric computation”, Mario Motta, Antonio Mezzacapo, Giacomo Guarnieri, [arXiv:2503.03868](https://doi.org/10.48550/arXiv:2503.03868). +15. “Noise-Robust Estimation of Quantum Observables in Noisy Hardware”, Amin Hosseinkhani, Fedor Šimkovic, Alessio Calzona, Tianhan Liu, Adrian Auer, Inés de Vega, [arXiv:2503.06695](https://doi.org/10.48550/arXiv:2503.06695). +16. “A Superconducting Qubit-Resonator Quantum Processor with Effective All-to-All Connectivity”, Michael Renger, Jeroen Verjauw, Nicola Wurz, Amin Hosseinkhani, Caspar Ockeloen-Korppi, Wei Liu, Aniket Rath, Manish J. Thapa, Florian Vigneau, Elisabeth Wybo, Ville Bergholm, Chun Fai Chan, Bálint Csatári, Saga Dahl, Rakhim Davletkaliyev, Rakshyakar Giri, Daria Gusenkova, Hermanni Heimonen, Tuukka Hiltunen, Hao Hsu, Eric Hyyppä, Joni Ikonen, Tyler Jones, Shabeeb Khalid, Seung-Goo Kim, Miikka Koistinen, Anton Komlev, Janne Kotilahti, Vladimir Kukushkin, Julia Lamprich, Alessandro Landra, Lan-Hsuan Lee, Tianyi Li, Per Liebermann, Sourav Majumder, Janne Mäntylä, Fabian Marxer, Arianne Meijer - van de Griend, Vladimir Milchakov, Jakub Mrożek, Jayshankar Nath, Tuure Orell, Miha Papič, Matti Partanen, Alexander Plyushch, Stefan Pogorzalek, Jussi Ritvas, Pedro Figuero Romero, Ville Sampo, Marko Seppälä, Ville Selinmaa, Linus Sundström, Ivan Takmakov, Brian Tarasinski, Jani Tuorila, Olli Tyrkkö, Alpo Välimaa, Jaap Wesdorp, Ping Yang, Liuqi Yu, Johannes Heinsoo, Antti Vepsäläinen, William Kindel, Hsiang-Sheng Ku, Frank Deppe, [arXiv:2503.10903](https://doi.org/10.48550/arXiv:2503.10903). +17. “Refining Noise Mitigation in NISQ Hardware Through Qubit Error Probability”, Nahual Sobrino, Unai Aseginolaza, Joaquim Jornet-Somoza, Juan Borge, [arXiv:2503.10204](https://doi.org/10.48550/arXiv:2503.10204). +18. “Entanglement teleportation along a regenerating hamster-wheel graph state”, Haiyue Kang, John F. Kam, Gary J. Mooney, Lloyd C. L. Hollenberg, [arXiv:2411.13060](https://doi.org/10.48550/arXiv.2411.13060). +19. “Ptychographic estimation of pure multiqubit states in a quantum device”, Warley M. S. Alves, Leonardo Neves, [APL Quantum 1, 046115 (2024)](https://doi.org/10.1063/5.0236968). +20. “Reducing PEC Overhead by Pauli Error Propagation”, Timon Scheiber, Paul Haubenwallner, Matthias Heller, [arXiv:2412.01311](https://doi.org/10.48550/arXiv.2412.01311). +21. “QSRA: A QPU Scheduling and Resource Allocation Approach for Cloud-Based Quantum Computing”, Binhan Lu, Zhaoyun Chen, Yuchun Wu, [arXiv:2411.05283](https://doi.org/10.48550/arXiv.2411.05283). +22. “Towards quantum computing Feynman diagrams in hybrid qubit-oscillator devices”, S. Varona, S. Saner, O. Băzăvan, G. Araneda, G. Aarts, A. Bermudez, [arXiv:2411.05092](https://doi.org/10.48550/arXiv.2411.05092). +23. “Quantum-centric computation of molecular excited states with extended sample-based quantum diagonalization”, Stefano Barison, Javier Robledo Moreno, Mario Motta, [arXiv:2411.00468](https://doi.org/10.48550/arXiv.2411.00468). +24. “Experimental demonstration of the Bell-type inequalities for four qubit Dicke state using IBM Quantum Processing Unit”, Tomis, Harsh Mehta, Shreya Banerjee, Prasanta K. Panigrahi, V. Narayanan, [arXiv:2410.20241](https://doi.org/10.48550/arXiv.2410.20241). +25. “Contextual Subspace Variational Quantum Eigensolver Calculation of the Dissociation Curve of Molecular Nitrogen on a Superconducting Quantum Computer”, Tim Weaving, Alexis Ralli, Peter J. Love, Sauro Succi, Peter V. Coveney, [arXiv:2312.04392](https://doi.org/10.48550/arXiv.2312.04392). +26. “Implementing Quantum Secret Sharing on Current Hardware”, Jay Graves, Mike Nelson, Eric Chitambar, [arXiv:2410.11640](https://doi.org/10.48550/arXiv.2410.11640). +27. “Statistics of topological defects across a phase transition in a superconducting quantum processor”, Daniil Teplitskiy, Oriel Kiss, Michele Grossi, Antonio Mandarino, [arXiv:2410.06250](https://doi.org/10.48550/arXiv.2410.06250). +28. “LatentQGAN: A Hybrid QGAN with Classical Convolutional Autoencoder”, Alexis Vieloszynski, Soumaya Cherkaoui, Jean-Frederic Laprade, Oliver Nahman-Levesque, Abdallah Aaraba, Shengrui Wang, [arXiv:2409.14622](https://doi.org/10.48550/arXiv.2409.14622). +29. “Experimental Sample-Efficient Quantum State Tomography via Parallel Measurements”, Chang-Kang Hu, Chao Wei, Chilong Liu, Liangyu Che, Yuxuan Zhou, Guixu Xie, Haiyang Qin, Guantian Hu, Haolan Yuan, Ruiyang Zhou, Song Liu, Dian Tan, Tao Xin, and Dapeng Yu, [Phys. Rev. Lett. 133, 160801 (2024)](https://doi.org/10.1103/PhysRevLett.133.160801). +30. “Solving the Hele-Shaw flow using the Harrow-Hassidim-Lloyd algorithm on superconducting devices: A study of efficiency and challenges”, Muralikrishnan Gopalakrishnan Meena, Kalyana C. Gottiparthi, Justin G. Lietz, Antigoni Georgiadou, Eduardo Antonio Coello Perez, [Physics of Fluids 36, 101705 (2024)](https://doi.org/10.1063/5.0231929). +31. “High-fidelity heralded quantum state preparation and measurement”, A. S. Sotirova, J. D. Leppard, A. Vazquez-Brennan, S. M. Decoppet, F. Pokorny, M. Malinowski, C. J. Ballance, [arXiv:2409.05805](https://doi.org/10.48550/arXiv.2409.05805). +32. “Practical techniques for high precision measurements on near-term quantum hardware: a Case Study in Molecular Energy Estimation”, Keijo Korhonen, Hetta Vappula, Adam Glos, Marco Cattaneo, Zoltan Zimboras, Elsi-Mari Borrelli, Matteo A. C. Rossi, Guillermo Garcia-Perez, Daniel Cavalcanti, [arXiv:2409.02575](https://doi.org/10.48550/arXiv.2409.02575). +33. “Purity-Assisted Zero-Noise Extrapolation for Quantum Error Mitigation”, Tian-Ren Jin, Yun-Hao Shi, Zheng-An Wang, Tian-Ming Li, Kai Xu, Heng Fan, [Adv Quantum Technol. 2024, 2400150](https://doi.org/10.1002/qute.202400150). +34. “Technology and Performance Benchmarks of IQM’s 20-Qubit Quantum Computer”, Leonid Abdurakhimov, Janos Adam, Hasnain Ahmad, Olli Ahonen, Manuel Algaba, Guillermo Alonso, Ville Bergholm, Rohit Beriwal, Matthias Beuerle, Clinton Bockstiegel, Alessio Calzona, Chun Fai Chan, Daniele Cucurachi, Saga Dahl, Rakhim Davletkaliyev, Olexiy Fedorets, Alejandro Gomez Frieiro, Zheming Gao, Johan Guldmyr, Andrew Guthrie, Juha Hassel, Hermanni Heimonen, Johannes Heinsoo, Tuukka Hiltunen, Keiran Holland, Juho Hotari, Hao Hsu, Antti Huhtala, Eric Hyyppä, Aleksi Hämäläinen, Joni Ikonen, Sinan Inel, David Janzso, Teemu Jaakkola, Mate Jenei, Shan Jolin, Kristinn Juliusson, Jaakko Jussila, Shabeeb Khalid, Seung-Goo Kim, Miikka Koistinen, Roope Kokkoniemi, Anton Komlev, Caspar Ockeloen-Korppi, Otto Koskinen, Janne Kotilahti, Toivo Kuisma, Vladimir Kukushkin, Kari Kumpulainen, Ilari Kuronen, Joonas Kylmälä, Niclas Lamponen, Julia Lamprich, Alessandro Landra, Martin Leib, Tianyi Li, Per Liebermann, Aleksi Lintunen, Wei Liu, Jürgen Luus, Fabian Marxer, Arianne Meijer-van de Griend, Kunal Mitra, Jalil Khatibi Moqadam, Jakub Mrożek, Henrikki Mäkynen, Janne Mäntylä, Tiina Naaranoja, Francesco Nappi, Janne Niemi, Lucas Ortega, Mario Palma, Miha Papič, Matti Partanen, Jari Penttilä, Alexander Plyushch, Wei Qiu, Aniket Rath, Kari Repo, Tomi Riipinen, Jussi Ritvas, Pedro Figueroa Romero, Jarkko Ruoho, Jukka Räbinä, Sampo Saarinen, Indrajeet Sagar, Hayk Sargsyan, Matthew Sarsby, Niko Savola, Mykhailo Savytskyi, Ville Selinmaa, Pavel Smirnov, Marco Marín Suárez, Linus Sundström, Sandra Słupińska, Eelis Takala, Ivan Takmakov, Brian Tarasinski, Manish Thapa, Jukka Tiainen et al., [arXiv:2408.12433](https://doi.org/10.48550/arXiv.2408.12433). +35. “Performance-Oriented Layout Synthesis for Quantum Computing”, Chi-Chou Kao and Hung-Yi Lin, [Computer Systems Science and Engineering (2024)](https://doi.org/10.32604/csse.2024.055073). +36. “Accurately Simulating the Time Evolution of an Ising Model with Echo Verified Clifford Data Regression on a Superconducting Quantum Computer”, Tim Weaving, Alexis Ralli, Peter J. Love, Sauro Succi, Peter V. Coveney, [arXiv:2408.07439](https://doi.org/10.48550/arXiv.2408.07439). +37. “Characterization of entanglement on superconducting quantum computers of up to 414 qubits”, John F. Kam, Haiyue Kang, Charles D. Hill, Gary J. Mooney, and Lloyd C. L. Hollenberg, [Phys. Rev. Research 6, 033155](https://doi.org/10.1103/PhysRevResearch.6.033155). +38. “Measurement-Based Long-Range Entangling Gates in Constant Depth”, Elisa Bäumer, Stefan Woerner, [arXiv:2408.03064](https://doi.org/10.48550/arXiv.2408.03064). +39. “Quantum Algorithms for the Variational Optimization of Correlated Electronic States with Stochastic Reconfiguration and the Linear Method”, Mario Motta, Kevin J. Sung, James Shee, [J. Phys. Chem. A 2024, 128, 40, 8762](https://doi.org/10.1021/acs.jpca.4c02847). +40. “Quantum noise modeling through Reinforcement Learning”, Simone Bordoni, Andrea Papaluca, Piergiorgio Buttarini, Alejandro Sopena, Stefano Giagu, Stefano Carrazza, [arXiv:2408.01506](https://doi.org/10.48550/arXiv.2408.01506). +41. “Multiaxis quantum noise spectroscopy robust to errors in state preparation and measurement”, Muhammad Qasim Khan, Wenzheng Dong, Leigh M. Norris, and Lorenza Viola, [Phys. Rev. Applied 22, 024074 (2024)](https://doi.org/10.1103/PhysRevApplied.22.024074). +42. “Bayesian mitigation of measurement errors in multi-qubit experiments”, F. Cosco, F. Plastina, N. Lo Gullo, [arXiv:2408.00869](https://doi.org/10.48550/arXiv.2408.00869). +43. “On the use of calibration data in error-aware compilation techniques for NISQ devices”, Handy Kurniawan, Laura Rodríguez-Soriano, Daniele Cuomo, Carmen G. Almudever, Francisco García Herrero, [arXiv:2407.21462](https://doi.org/10.48550/arXiv.2407.21462). +44. “Quantum simulation of entanglement dynamics in a quantum processor”, Sebastián Saavedra-Pino, Cristian Inzulza, Pablo Román, Francisco Albarrán-Arriagada, and Juan Carlos Retamal, [Phys. Scr. 99 095105 (2024)](https://doi.org/10.1088/1402-4896/ad624a). +45. “A Noise Validation for Quantum Circuit Scheduling Through a Service-Oriented Architecture”, Javier Romero-Álvarez, Jaime Alvarado-Valiente, Jorge Casco-Seco, Enrique Moguel, Jose Garcia-Alonso, and Juan M. Murillo, [International Journal of Software Engineering and Knowledge Engineering 2024](https://doi.org/10.1142/S0218194024410018). +46. “Variational Gibbs state preparation on noisy intermediate-scale quantum devices”, Mirko Consiglio, Jacopo Settino, Andrea Giordano, Carlo Mastroianni, Francesco Plastina, Salvatore Lorenzo, Sabrina Maniscalco, John Goold, and Tony J. G. Apollaro, [Phys. Rev. A 110, 012445 (2024)](https://doi.org/10.1103/PhysRevA.110.012445). +47. “Microscopic parametrizations for gate set tomography under coloured noise”, P. Viñas, A. Bermudez, [arXiv:2407.11539](https://doi.org/10.48550/arXiv.2407.11539). +48. “Superconducting Quantum Simulation for Many-Body Physics beyond Equilibrium”, Yunyan Yao and Liang Xiang, [Entropy 2024, 26(7), 592](https://doi.org/10.3390/e26070592). +49. “Teleporting two-qubit entanglement across 19 qubits on a superconducting quantum computer”, Haiyue Kang, John F. Kam, Gary J. Mooney, Lloyd C. L. Hollenberg, [arXiv:2407.02858](https://doi.org/10.48550/arXiv.2407.02858). +50. “Realizing string-net condensation: Fibonacci anyon braiding for universal gates and sampling chromatic polynomials”, Zlatko K. Minev, Khadijeh Najafi, Swarnadeep Majumder, Juven Wang, Ady Stern, Eun-Ah Kim, Chao-Ming Jian, Guanyu Zhu, [arXiv:2406.12820](https://doi.org/10.48550/arXiv.2406.12820). +51. “Readout Error Mitigation for Mid-Circuit Measurements and Feedforward”, Jin Ming Koh, Dax Enshan Koh, Jayne Thompson, [arXiv:2406.07611](https://doi.org/10.48550/arXiv.2406.07611). +52. “Ground-state-energy calculation for the water molecule on a superconducting quantum processor”, Michael A. Jones, Harish J. Vallury, and Lloyd C.L. Hollenberg, [Phys. Rev. Applied 21, 064017 (2024)](https://doi.org/10.1103/PhysRevApplied.21.064017). +53. “mRNA secondary structure prediction using utility-scale quantum computers”, Dimitris Alevras, Mihir Metkar, Takahiro Yamamoto, Vaibhaw Kumar, Triet Friedhoff, Jae-Eun Park, Mitsuharu Takeori, Mariana LaDue, Wade Davis, Alexey Galda, [arXiv:2405.20328](https://doi.org/10.48550/arXiv.2405.20328). +54. “Full Band Structure Calculation of Semiconducting Materials on a Noisy Quantum Processor”, Shaobo Zhang, Akib Karim, Harry M. Quiney, Muhammad Usman, [arXiv:2405.09122](https://doi.org/10.48550/arXiv.2405.09122). +55. “Feed-Forward Probabilistic Error Cancellation with Noisy Recovery Gates”, Leo Kurosawa, Yoshiyuki Saito, Xinwei Lee, Xinjian Yan, Ningyi Xie, Dongsheng Cai, Jungpil Shin, Nobuyoshi Asai, [arXiv:2405.01833](https://doi.org/10.48550/arXiv.2405.01833). +56. “Sachdev-Ye-Kitaev model on a noisy quantum computer”, Muhammad Asaduzzaman, Raghav G. Jha, and Bharath Sambasivam, [Phys. Rev. D 109, 105002 (2024)](https://doi.org/10.1103/PhysRevD.109.105002). +57. “Simulation of a spin-boson model by iterative optimization of a parametrized quantum circuit”, Takanori Nishi, Kaoru Yamanouchi , [AVS Quantum Sci. 6, 023801 (2024)](https://doi.org/10.1116/5.0193981). +58. “Benchmarking digital quantum simulations above hundreds of qubits using quantum critical dynamics”, Alexander Miessen, Daniel J. Egger, Ivano Tavernelli, Guglielmo Mazzola, [arXiv:2404.08053](https://doi.org/10.48550/arXiv.2404.08053). +59. “Quantum Tunneling: From Theory to Error-Mitigated Quantum Simulation”, Sorana Catrina, Alexandra Băicoianu, [Adv. Quantum Technol. 2024, 2400163](https://doi.org/10.1002/qute.202400163). +60. “Analyzing the Temporal Behavior of Noisy Intermediate-Scale Quantum Nodes and Algorithm Fidelity”, Carlo Podda, Giuliana Siddi Moreau, Lorenzo Pisani, Lidia Leoni, Giacomo Cao, [Adv. Quantum Technol. 2024, 2300451](https://doi.org/10.1002/qute.202300451). +61. “Surrogate optimization of variational quantum circuits”, Erik J. Gustafson, Juha Tiihonen, Diana Chamaki, Farshud Sorourifar, J. Wayne Mullinax, Andy C. Y. Li, Filip B. Maciejewski, Nicolas PD Sawaya, Jaron T. Krogel, David E. Bernal Neira, Norm M. Tubman, [arXiv:2404.02951](https://doi.org/10.48550/arXiv.2404.02951). +62. “Quantum circuit scheduler for QPUs usage optimization”, Javier Romero-Alvarez, Jaime Alvarado-Valiente, Jorge Casco-Seco, Enrique Moguel, Jose Garcia-Alonso, Javier Berrocal, Juan M. Murillo, [arXiv:2404.01055](https://doi.org/10.48550/arXiv.2404.01055). +63. “Circuit-noise-resilient virtual distillation”, Xu, XY., Ding, C., Zhang, S. et al., [Commun Phys 7, 325 (2024)](https://doi.org/10.1038/s42005-024-01815-2). +64. “Lindblad-like quantum tomography for non-Markovian quantum dynamical maps”, Santiago Varona, Markus Müller, Alejandro Bermudez, [arXiv:2403.19799](https://doi.org/10.48550/arXiv.2403.19799). +65. “Quantum-Enhanced Simulation-Based Optimization for Newsvendor Problems”, Monit Sharma, Hoong Chuin Lau, Rudy Raymond, [arXiv:2403.17389](https://doi.org/10.48550/arXiv.2403.17389). +66. “Quantum State Preparation for Probability Distributions with Mirror Symmetry Using Matrix Product States”, Yuichi Sano, Ikko Hamamura, [arXiv:2403.16729](https://doi.org/10.48550/arXiv.2403.16729). +67. “Quantum Fourier Transform Using Dynamic Circuits”, Elisa Bäumer, Vinay Tripathi, Alireza Seif, Daniel Lidar, and Derek S. Wang, [Phys. Rev. Lett. 133, 150602 (2024)](https://doi.org/10.1103/PhysRevLett.133.150602). +68. “Simulation of a Diels-Alder Reaction on a Quantum Computer”, Ieva Liepuoniute, Mario Motta, Thaddeus Pellegrini, Julia E. Rice, Tanvi P. Gujarati, Sofia Gil, Gavin O. Jones, [arXiv:2403.08107](https://doi.org/10.48550/arXiv.2403.08107). +69. “Low-Rank Variational Quantum Algorithm for the Dynamics of Open Quantum Systems”, Sara Santos, Xinyu Song, Vincenzo Savona, [arXiv:2403.05908](https://doi.org/10.48550/arXiv.2403.05908). +70. “Quantum many-body simulations on digital quantum computers: State-of-the-art and future challenges”, Benedikt Fauseweh, [Nat Commun 15, 2123 (2024)](https://doi.org/10.1038/s41467-024-46402-9). +71. “Variational quantum eigensolver with linear depth problem-inspired ansatz for solving portfolio optimization in finance”, Shengbin Wang, Peng Wang, Guihui Li, Shubin Zhao, Dongyi Zhao, Jing Wang, Yuan Fang, Menghan Dou, Yongjian Gu, Yu-Chun Wu, Guo-Ping Guo, [arXiv:2403.04296](https://doi.org/10.48550/arXiv.2403.04296). +72. “An Energy Estimation Benchmark for Quantum Computing Hardware”, Andreas J. C. Woitzik, Lukas Hoffmann, Andreas Buchleitner, and Edoardo G. Carnio, [Open Systems & Information Dynamics 2024 31:01](https://doi.org/10.1142/S1230161224500069). +73. “Empirical learning of dynamical decoupling on quantum processors”, Christopher Tong, Helena Zhang, Bibek Pokharel, [arXiv:2403.02294](https://doi.org/10.48550/arXiv.2403.02294). +74. “Scalable measurement error mitigation via iterative bayesian unfolding”, Bibek Pokharel, Siddarth Srinivasan, Gregory Quiroz, and Byron Boots, [Phys. Rev. Research 6, 013187 (2024)](https://doi.org/10.1103/PhysRevResearch.6.013187). +75. “Maximum Likelihood Quantum Error Mitigation for Algorithms with a Single Correct Output”, Dror Baron, Hrushikesh Pramod Patil, Huiyang Zhou, [arXiv:2402.11830](https://doi.org/10.48550/arXiv.2402.11830). +76. “Scalable Quantum Algorithms for Noisy Quantum Computers”, Julien Gacon, [arXiv:2403.00940](https://doi.org/10.48550/arXiv.2403.00940). +77. “Quantum Algorithm Exploration using Application-Oriented Performance Benchmarks”, Thomas Lubinski, Joshua J. Goings, Karl Mayer, Sonika Johri, Nithin Reddy, Aman Mehta, Niranjan Bhatia, Sonny Rappaport, Daniel Mills, Charles H. Baldwin, Luning Zhao, Aaron Barbosa, Smarak Maity, Pranav S. Mundada, [arXiv:2402.08985](https://doi.org/10.48550/arXiv.2402.08985). +78. “On-premises superconducting quantum computer for education and research”, Jami Rönkkö, Olli Ahonen, Ville Bergholm, Alessio Calzona, Attila Geresdi, Hermanni Heimonen, Johannes Heinsoo, Vladimir Milchakov, Stefan Pogorzalek, Matthew Sarsby, Mykhailo Savytskyi, Stefan Seegerer, Fedor Šimkovic, P. V. Sriluckshmy, Panu T. Vesanen and Mikio Nakahara, [EPJ Quantum Technol., 11 1 (2024) 32](https://doi.org/10.1140/epjqt/s40507-024-00243-z). +79. “Robust projective measurements through measuring code-inspired observables”, Yingkai Ouyang, [arXiv:2402.04093](https://doi.org/10.48550/arXiv.2402.04093). +80. “Comparative study of quantum error correction strategies for the heavy-hexagonal lattice”, César Benito, Esperanza López, Borja Peropadre, Alejandro Bermudez, [arXiv:2402.02185](https://doi.org/10.48550/arXiv.2402.02185). +81. “Assessing the Benefits and Risks of Quantum Computers”, Travis L. Scholten, Carl J. Williams, Dustin Moody, Michele Mosca, William Hurley (“whurley”), William J. Zeng, Matthias Troyer, Jay M. Gambetta, [arXiv:2401.16317](https://doi.org/10.48550/arXiv.2401.16317). +82. “Quantifying the effect of gate errors on variational quantum eigensolvers for quantum chemistry”, Dalton, K., Long, C.K., Yordanov, Y.S. et al., [npj Quantum Inf 10, 18 (2024)](https://doi.org/10.1038/s41534-024-00808-x). +83. “Quantum error mitigation for Fourier moment computation”, Oriel Kiss, Michele Grossi, Alessandro Roggero, [arXiv:2401.13048](https://doi.org/10.48550/arXiv.2401.13048). +84. “Quantum simulations of hadron dynamics in the Schwinger model using 112 qubits”, Roland C. Farrell, Marc Illa, Anthony N. Ciavarella, and Martin J. Savage, [Phys. Rev. D 109, 114510 (2024)](https://doi.org/10.1103/PhysRevD.109.114510). +85. “Quantum simulation of the one-dimensional Fermi-Hubbard model as a Z2 lattice-gauge theory”, Uliana E. Khodaeva, Dmitry L. Kovrizhin, and Johannes Knolle, [Phys. Rev. Research 6, 013032 (2024)](https://doi.org/10.1103/PhysRevResearch.6.013032). +86. “Applying the noiseless extrapolation error mitigation protocol to calculate real-time quantum field theory scattering phase shifts”, Zachary Parks, Arnaud Carignan-Dugas, Erik Gustafson, Yannick Meurice, and Patrick Dreher, [Phys. Rev. D 109, 014505 (2024)](https://doi.org/10.1103/PhysRevD.109.014505). +87. “Nonlinear dynamics as a ground-state solution on quantum computers”, Albert J. Pool, Alejandro D. Somoza, Conor Mc Keever, Michael Lubasch, and Birger Horstmann, [Phys. Rev. Research 6, 033257 (2024)](https://doi.org/10.1103/PhysRevResearch.6.033257). +88. “High-Fidelity, Multiqubit Generalized Measurements with Dynamic Circuits”, Petr Ivashkov, Gideon Uchehara, Liang Jiang, Derek S. Wang, and Alireza Seif, [PRX Quantum 5, 030315 (2024)](https://doi.org/10.1103/PRXQuantum.5.030315). +89. “SantaQlaus: A resource-efficient method to leverage quantum shot-noise for optimization of variational quantum algorithms”, Kosuke Ito, Keisuke Fujii, [arXiv:2312.15791](https://doi.org/10.48550/arXiv.2312.15791). +90. “Quantum error mitigation and correction mediated by Yang-Baxter equation and artificial neural network”, Sahil Gulania, Yuri Alexeev, Stephen K. Gray, Bo Peng, Niranjan Govind, [arXiv:2401.17116](https://doi.org/10.48550/arXiv.2401.17116). +91. “Inverted-circuit zero-noise extrapolation for quantum gate error mitigation”, Kathrin F. Koenig, Finn Reinecke, Walter Hahn, Thomas Wellens, [arXiv:2403.01608](https://doi.org/10.48550/arXiv.2403.01608). +92. “Quantum State Compression Shadow”, Chen Ding, Xiao-Yue Xu, Shuo Zhang, Wan-Su Bao, He-Liang Huang, [arXiv:2312.13036](https://doi.org/10.48550/arXiv.2312.13036). +93. “Enhancing quantum utility: Simulating large-scale quantum spin chains on superconducting quantum computers”, Talal Ahmed Chowdhury, Kwangmin Yu, Mahmud Ashraf Shamim, M. L. Kabir, and Raza Sabbir Sufian, [Phys. Rev. Research 6, 033107 (2024)](https://doi.org/10.1103/PhysRevResearch.6.033107). +94. “Subspace methods for electronic structure simulations on quantum computers”, Mario Motta, William Kirby, Ieva Liepuoniute, Kevin J Sung, Jeffrey Cohn, Antonio Mezzacapo, Katherine Klymko, Nam Nguyen, Nobuyuki Yoshioka, and Julia E Rice, [Electron. Struct. 6 013001 (2024)](https://doi.org/10.1088/2516-1075/ad3592). +95. “Provable bounds for noise-free expectation values computed from noisy samples”, Samantha V. Barron, Daniel J. Egger, Elijah Pelofske, Andreas Bärtschi, Stephan Eidenbenz, Matthis Lehmkuehler, Stefan Woerner, [arXiv:2312.00733](https://doi.org/10.48550/arXiv.2312.00733). +96. “Exploiting Maximally Mixed States for Spectral Estimation by Time Evolution”, Kaelyn J. Ferris, Zihang Wang, Itay Hen, Amir Kalev, Nicholas T. Bronn, Vojtech Vlcek, [arXiv:2312.00687](https://doi.org/10.48550/arXiv.2312.00687). +97. “Quantum simulations for strong-field QED”, Luis Hidalgo and Patrick Draper, [Phys. Rev. D 109, 076004 (2024)](https://doi.org/10.1103/PhysRevD.109.076004). +98. “Quantum Simulation of an Open System: A Dissipative 1+1D Ising Model”, Erik Gustafson, Michael Hite, Jay Hubisz, Bharath Sambasivam, Judah Unmuth-Yockey, [arXiv:2311.18728](https://doi.org/10.48550/arXiv.2311.18728). +99. “Improving the performance of digitized counterdiabatic quantum optimization via algorithm-oriented qubit mapping”, Yanjun Ji, Kathrin F. Koenig, and Ilia Polian, [Phys. Rev. A 110, 032421 (2024)](https://doi.org/10.1103/PhysRevA.110.032421). +100. “Quantum Diffusion Models”, Andrea Cacioppo, Lorenzo Colantonio, Simone Bordoni, Stefano Giagu, [arXiv:2311.15444](https://doi.org/10.48550/arXiv.2311.15444). +101. “An approach to solve the coarse-grained Protein folding problem in a Quantum Computer”, Jaya Vasavi P, Soham Bopardikar, Avinash D, Ashwini K, Kalyan Dasgupta, Sanjib Senapati, [arXiv:2311.14141](https://doi.org/10.48550/arXiv.2311.14141). +102. “Perspectives of running self-consistent DMFT calculations for strongly correlated electron systems on noisy quantum computing hardware”, Jannis Ehrlich, Daniel Urban, Christian Elsässer, [arXiv:2311.10402](https://doi.org/10.48550/arXiv.2311.10402). +103. “Observation of the non-Hermitian skin effect and Fermi skin on a digital quantum computer”, Ruizhe Shen, Tianqi Chen, Bo Yang, Ching Hua Lee, [arXiv:2311.10143](https://doi.org/10.48550/arXiv.2311.10143). +104. “Comparison of current quantum devices for quantum computing of Heisenberg spin chain dynamics”, Erik Lötstedt and Kaoru Yamanouchi, [Chemical Physics Letters 836, 140975 (2024)](https://doi.org/10.1016/j.cplett.2023.140975). +105. “ADAPT-QSCI: Adaptive Construction of Input State for Quantum-Selected Configuration Interaction”, Yuya O. Nakagawa, Masahiko Kamoshita, Wataru Mizukami, Shotaro Sudo, Yu-ya Ohnishi, [arXiv:2311.01105](https://doi.org/10.48550/arXiv.2311.01105). +106. “Efficient separate quantification of state preparation errors and measurement errors on quantum computers and their mitigation”, Hongye Yu, Tzu-Chieh Wei, [arXiv:2310.18881](https://doi.org/10.48550/arXiv.2310.18881). +107. “Quantum error mitigation”, Zhenyu Cai, Ryan Babbush, Simon C. Benjamin, Suguru Endo, William J. Huggins, Ying Li, Jarrod R. McClean, and Thomas E. O’Brien, [Rev. Mod. Phys. 95, 045005 (2023)](https://doi.org/10.1103/RevModPhys.95.045005). +108. “Quantum Simulation for High-Energy Physics”, Christian W. Bauer et al., [PRX Quantum 4, 027001 (2023)](https://doi.org/10.1103/PRXQuantum.4.027001). +109. “Scalable Circuits for Preparing Ground States on Digital Quantum Computers: The Schwinger Model Vacuum on 100 Qubits”, Roland C. Farrell, Marc Illa, Anthony N. Ciavarella, and Martin J. Savage, [PRX Quantum 5, 020315 (2024)](https://doi.org/10.1103/PRXQuantum.5.020315). +110. “Near-term quantum computing techniques: Variational quantum algorithms, error mitigation, circuit compilation, benchmarking and classical simulation”, Huang, HL., Xu, XY., Guo, C. et al., [Sci. China Phys. Mech. Astron. 66, 250302 (2023)](https://doi.org/10.1007/s11433-022-2057-y). +111. “Scaling of the quantum approximate optimization algorithm on superconducting qubit based hardware”, Johannes Weidenfeller, Lucia C. Valor, Julien Gacon, Caroline Tornow, Luciano Bello, Stefan Woerner, Daniel J. Egger, [Quantum 6, 870 (2022)](https://doi.org/10.22331/q-2022-12-07-870). +112. “Deterministic Constant-Depth Preparation of the AKLT State on a Quantum Processor Using Fusion Measurements”, Kevin C. Smith, Eleanor Crane, Nathan Wiebe, and S.M. Girvin, [PRX Quantum 4, 020315 (2023)](https://doi.org/10.1103/PRXQuantum.4.020315). +113. “Biology and medicine in the landscape of quantum advantages”, Benjamin A. Cordier, Nicolas P. D. Sawaya, Gian Giacomo Guerreschi and Shannon K. McWeeney, [J. R. Soc. Interface.1920220541](https://doi.org/10.1098/rsif.2022.0541). +114. “Quantum computing of the 6Li nucleus via ordered unitary coupled clusters”, Oriel Kiss, Michele Grossi, Pavel Lougovski, Federico Sanchez, Sofia Vallecorsa, and Thomas Papenbrock, [Phys. Rev. C 106, 034325 (2022)](https://doi.org/10.1103/PhysRevC.106.034325). +115. “Demonstration of Algorithmic Quantum Speedup”, Bibek Pokharel and Daniel A. Lidar, [Phys. Rev. Lett. 130, 210602 (2023)](https://doi.org/10.1103/PhysRevLett.130.210602). +116. “Digitized Counterdiabatic Quantum Algorithm for Protein Folding”, Pranav Chandarana, Narendra N. Hegade, Iraitz Montalban, Enrique Solano, and Xi Chen, [Phys. Rev. Applied 20, 014024 (2023)](https://doi.org/10.1103/PhysRevApplied.20.014024). +117. “Universal Sampling Lower Bounds for Quantum Error Mitigation”, Ryuji Takagi, Hiroyasu Tajima, and Mile Gu, [Phys. Rev. Lett. 131, 210602 (2023)](https://doi.org/10.1103/PhysRevLett.131.210602). +118. “Measurement error mitigation in quantum computers through classical bit-flip correction”, Lena Funcke, Tobias Hartung, Karl Jansen, Stefan Kühn, Paolo Stornati, and Xiaoyang Wang, [Phys. Rev. A 105, 062404 (2022)](https://doi.org/10.1103/PhysRevA.105.062404). +119. “Measuring nonstabilizerness via multifractal flatness”, Xhek Turkeshi, Marco Schirò, and Piotr Sierant, [Phys. Rev. A 108, 042408 (2023)](https://doi.org/10.1103/PhysRevA.108.042408). +120. “Experimental Benchmarking of an Automated Deterministic Error-Suppression Workflow for Quantum Algorithms”, Pranav S. Mundada, Aaron Barbosa, Smarak Maity, Yulun Wang, Thomas Merkh, T.M. Stace, Felicity Nielson, Andre R.R. Carvalho, Michael Hush, Michael J. Biercuk, and Yuval Baum, [Phys. Rev. Applied 20, 024034 (2023)](https://doi.org/10.1103/PhysRevApplied.20.024034). +121. “Uncovering Local Integrability in Quantum Many-Body Dynamics”, Oles Shtanko, Derek S. Wang, Haimeng Zhang, Nikhil Harle, Alireza Seif, Ramis Movassagh, Zlatko Minev, [arXiv:2307.07552](https://doi.org/10.48550/arXiv.2307.07552). +122. “Dissipative Dynamics of Graph-State Stabilizers with Superconducting Qubits”, Liran Shirizly, Grégoire Misguich, and Haggai Landa, [Phys. Rev. Lett. 132, 010601 (2024)](https://doi.org/10.1103/PhysRevLett.132.010601). +123. “Blueprint for a Molecular-Spin Quantum Processor”, A. Chiesa, S. Roca, S. Chicco, M.C. de Ory, A. Gómez-León, A. Gomez, D. Zueco, F. Luis, and S. Carretta, [Phys. Rev. Applied 19, 064060 (2023)](https://doi.org/10.1103/PhysRevApplied.19.064060). +124. “Primitive quantum gates for dihedral gauge theories”, M. Sohaib Alam, Stuart Hadfield, Henry Lamm, and Andy C. Y. Li (SQMS Collaboration), [Phys. Rev. D 105, 114501 (2022)](https://doi.org/10.1103/PhysRevD.105.114501). +125. “Pulse variational quantum eigensolver on cross-resonance-based hardware”, Daniel J. Egger, Chiara Capecci, Bibek Pokharel, Panagiotis Kl. Barkoutsos, Laurin E. Fischer, Leonardo Guidoni, and Ivano Tavernelli, [Phys. Rev. Research 5, 033159 (2023)](https://doi.org/10.1103/PhysRevResearch.5.033159). +126. “Simulating large-size quantum spin chains on cloud-based superconducting quantum computers”, Hongye Yu (余泓烨), Yusheng Zhao, and Tzu-Chieh Wei, [Phys. Rev. Research 5, 013183 (2023)](https://doi.org/10.1103/PhysRevResearch.5.013183). +127. “Steering-enhanced quantum metrology using superpositions of noisy phase shifts”, Kuan-Yi Lee, Jhen-Dong Lin, Adam Miranowicz, Franco Nori, Huan-Yu Ku, and Yueh-Nan Chen, [Phys. Rev. Research 5, 013103 (2023)](https://doi.org/10.1103/PhysRevResearch.5.013103). +128. “Effective calculation of the Green’s function in the time domain on near-term quantum processors”, Francesco Libbi, Jacopo Rizzo, Francesco Tacchino, Nicola Marzari, and Ivano Tavernelli, [Phys. Rev. Research 4, 043038 (2022)](https://doi.org/10.1103/PhysRevResearch.4.043038). +129. “N-Electron Valence Perturbation Theory with Reference Wave Functions from Quantum Computing: Application to the Relative Stability of Hydroxide Anion and Hydroxyl Radical”, Alessandro Tammaro, Davide E. Galli, Julia E. Rice, Mario Motta, [J. Phys. Chem. A 2023, 127, 3, 817–827](https://doi.org/10.1021/acs.jpca.2c07653). +130. “Efficient quantum readout-error mitigation for sparse measurement outcomes of near-term quantum devices”, Bo Yang, Rudy Raymond, and Shumpei Uno, [Phys. Rev. A 106, 012423 (2022)](https://doi.org/10.1103/PhysRevA.106.012423). +131. “Finite-size criticality in fully connected spin models on superconducting quantum hardware”, Michele Grossi, Oriel Kiss, Francesco De Luca, Carlo Zollo, Ian Gremese, and Antonio Mandarino, [Phys. Rev. E 107, 024113 (2023)](https://doi.org/10.1103/PhysRevE.107.024113). +132. “Hybrid Gate-Pulse Model for Variational Quantum Algorithms”, Zhiding Liang; Zhixin Song; Jinglei Cheng; Zichang He; Ji Liu; Hanrui Wang, [60th ACM/IEEE Design Automation Conference (DAC) (2023)](https://doi.org/10.1109/DAC56929.2023.10247923). +133. “Computing the Many-Body Green’s Function with Adaptive Variational Quantum Dynamics”, Niladri Gomes, David B. Williams-Young, Wibe A. de Jong, [J. Chem. Theory Comput. 2023, 19, 11, 3313](https://doi.org/10.1021/acs.jctc.3c00150). +134. “Characterizing Crosstalk of Superconducting Transmon Processors”, Andreas Ketterer and Thomas Wellens, [Phys. Rev. Applied 20, 034065 (2023)](https://doi.org/10.1103/PhysRevApplied.20.034065). +135. “Preparing valence-bond-solid states on noisy intermediate-scale quantum computers”, Bruno Murta, Pedro M. Q. Cruz, and J. Fernández-Rossier, [Phys. Rev. Research 5, 013190 (2023)](https://doi.org/10.1103/PhysRevResearch.5.013190). +136. “Configurable Readout Error Mitigation in Quantum Workflows “, Beisel M, Barzen J, Leymann F, Truger F, Weder B, Yussupov V, [Electronics. 2022; 11(19):2983](https://doi.org/10.3390/electronics11192983). +137. “Quantum Algorithm for Imaginary-Time Green’s Functions”, Diksha Dhawan, Dominika Zgid, Mario Motta, [J. Chem. Theory Comput. 2024, 20, 11, 4629](https://doi.org/10.1021/acs.jctc.4c00241). +138. “Best Practices for Quantum Error Mitigation with Digital Zero-Noise Extrapolation”, Ritajit Majumdar; Pedro Rivero; Friedrike Metz; Areeq Hasan; Derek S. Wang, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://doi.org/10.1109/QCE57702.2023.00102). +139. “Conditional Born machine for Monte Carlo event generation”, Oriel Kiss, Michele Grossi, Enrique Kajomovitz, and Sofia Vallecorsa, [Phys. Rev. A 106, 022612 (2022)](https://doi.org/10.1103/PhysRevA.106.022612). +140. “Quantum approximate optimization via learning-based adaptive optimization”, Cheng, L., Chen, YQ., Zhang, SX. et al., [Commun Phys 7, 83 (2024)](https://doi.org/10.1038/s42005-024-01577-x). +141. “Folded Spectrum VQE: A Quantum Computing Method for the Calculation of Molecular Excited States”, Lila Cadi Tazi, Alex J. W. Thom, [J. Chem. Theory Comput. 2024, 20, 6, 2491](https://doi.org/10.1021/acs.jctc.3c01378). +142. “Snowmass White Paper: Quantum Computing Systems and Software for High-energy Physics Research”, Travis S. Humble, Andrea Delgado, Raphael Pooser, Christopher Seck, Ryan Bennink, Vicente Leyton-Ortega, C.-C. Joseph Wang, Eugene Dumitrescu, Titus Morris, Kathleen Hamilton, Dmitry Lyakh, Prasanna Date, Yan Wang, Nicholas A. Peters, Katherine J. Evans, Marcel Demarteau, Alex McCaskey, Thien Nguyen, Susan Clark, Melissa Reville, Alberto Di Meglio, Michele Grossi, Sofia Vallecorsa, Kerstin Borras, Karl Jansen, Dirk Krücker, [arXiv:2203.07091](https://doi.org/10.48550/arXiv.2203.07091). +143. “Adaptive POVM implementations and measurement error mitigation strategies for near-term quantum devices”, Adam Glos, Anton Nykänen, Elsi-Mari Borrelli, Sabrina Maniscalco, Matteo A. C. Rossi, Zoltán Zimborás, Guillermo García-Pérez, [arXiv:2208.07817](https://doi.org/10.48550/arXiv.2208.07817). +144. “Quantum Gaussian process regression for Bayesian optimization”, Frederic Rapp & Marco Roth, [Quantum Mach. Intell. 6, 5 (2024)](https://doi.org/10.1007/s42484-023-00138-9). +145. “Quantum Ising model on two-dimensional anti–de Sitter space”, Muhammad Asaduzzaman, Simon Catterall, Yannick Meurice, and Goksu Can Toga, [Phys. Rev. D 109, 054513 (2024)](https://doi.org/10.1103/PhysRevD.109.054513). +146. “Advances in Quantum Computation and Quantum Technologies: A Design Automation Perspective”, G. De Micheli, J. -H. R. Jiang, R. Rand, K. Smith and M. Soeken, [IEEE Journal on Emerging and Selected Topics in Circuits and Systems, vol. 12, no. 3, pp. 584 (2022)](https://doi.org/10.1109/JETCAS.2022.3205174). +147. “Performance Study of Variational Quantum Algorithms for Solving the Poisson Equation on a Quantum Computer”, Mazen Ali and Matthias Kabel, [Phys. Rev. Applied 20, 014054 (2023)](https://doi.org/10.1103/PhysRevApplied.20.014054). +148. “Leveraging quantum computing for dynamic analyses of logical networks in systems biology”, Weidner, Felix M. et al., [Patterns, Volume 4, Issue 3, 100705 (2023)](https://doi.org/10.1016/j.patter.2023.100705). +149. “Improved financial forecasting via quantum machine learning”, Thakkar, S., Kazdaghli, S., Mathur, N. et al., [Quantum Mach. Intell. 6, 27 (2024)](https://doi.org/10.1007/s42484-024-00157-0). +150. “Evaluating the resilience of variational quantum algorithms to leakage noise”, Chen Ding, Xiao-Yue Xu, Shuo Zhang, He-Liang Huang, and Wan-Su Bao, [Phys. Rev. A 106, 042421 (2022)](https://doi.org/10.1103/PhysRevA.106.042421). +151. “Characterizing and mitigating coherent errors in a trapped ion quantum processor using hidden inverses”, Majumder, Swarnadeep and Yale, Christopher G. and Morris, Titus D. and Lobser, Daniel S. and Burch, Ashlyn D. and Chow, Matthew N. H. and Revelle, Melissa C. and Clark, Susan M. and Pooser, Raphael C., [Quantum 7, 1006 (2023)](https://doi.org/10.22331/q-2023-05-15-1006). +152. “Demonstrating quantum computation for quasiparticle band structures”, Takahiro Ohgoe, Hokuto Iwakiri, Masaya Kohda, Kazuhide Ichikawa, Yuya O. Nakagawa, Hubert Okadome Valencia, and Sho Koh, [Phys. Rev. Research 6, L022022 (2024)](https://doi.org/10.1103/PhysRevResearch.6.L022022). +153. “Ising meson spectroscopy on a noisy digital quantum simulator”, Lamb, C., Tang, Y., Davis, R. et al., [Nat Commun 15, 5901 (2024)](https://doi.org/10.1038/s41467-024-50206-2). +154. “Adaptive quantum error mitigation using pulse-based inverse evolutions”, Henao, I., Santos, J.P. & Uzdin, R., [npj Quantum Inf 9, 120 (2023)](https://doi.org/10.1038/s41534-023-00785-7). +155. “Quantum Natural Policy Gradients: Towards Sample-Efficient Reinforcement Learning”, N. Meyer, D. D. Scherer, A. Plinge, C. Mutschler and M. J. Hartmann, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://doi.org/10.1109/QCE57702.2023.10181). +156. “Extending the variational quantum eigensolver to finite temperatures”, Johannes Selisko et al, [2024 Quantum Sci. Technol. 9 015026](https://doi.org/10.1088/2058-9565/ad1340). +157. “A Robust Large-Period Discrete Time Crystal and its Signature in a Digital Quantum Computer”, Tianqi Chen, Ruizhe Shen, Ching Hua Lee, Bo Yang, Raditya Weda Bomantara, [arXiv:2309.11560](https://doi.org/10.48550/arXiv.2309.11560). +158. “Explaining Quantum Circuits with Shapley Values: Towards Explainable Quantum Machine Learning”, Raoul Heese, Thore Gerlach, Sascha Mücke, Sabine Müller, Matthias Jakobs, Nico Piatkowski, [arXiv:2301.09138](https://doi.org/10.48550/arXiv.2301.09138). +159. “Shallow unitary decompositions of quantum Fredkin and Toffoli gates for connectivity-aware equivalent circuit averaging”, Pedro M. Q. Cruz, Bruno Murta, [APL Quantum 1, 016105 (2024)](https://doi.org/10.1063/5.0187026). +160. “A Bayesian Approach for Characterizing and Mitigating Gate and Measurement Errors”, Zheng, Muqing and Li, Ang and Terlaky, Tamas and Yang, Xiu, [ACM Transactions on Quantum Computing 4, 21 (2023)](https://doi.org/10.1145/3563397). +161. “Quantum simulations of molecular systems with intrinsic atomic orbitals”, Stefano Barison, Davide E. Galli, and Mario Motta, [Phys. Rev. A 106, 022404 (2023)](https://doi.org/10.1103/PhysRevA.106.022404). +162. “Hardware-Tailored Diagonalization Circuits”, Daniel Miller, Laurin E. Fischer, Kyano Levi, Eric J. Kuehnke, Igor O. Sokolov, Panagiotis Kl. Barkoutsos, Jens Eisert, Ivano Tavernelli, [arXiv:2203.03646](https://doi.org/10.48550/arXiv.2203.03646). +163. “Information-theoretic approach to readout-error mitigation for quantum computers”, Hai-Chau Nguyen, [Phys. Rev. A 108, 052419 (2023)](https://doi.org/10.1103/PhysRevA.108.052419). +164. “Defining Best Practices for Quantum Benchmarks”, Mirko Amico; Helena Zhang; Petar Jurcevic; Lev S. Bishop; Paul Nation; Andrew Wack, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://doi.org/10.1109/QCE57702.2023.00084). +165. “Simulating Majorana zero modes on a noisy quantum processor”, Kevin J Sung et al , [2023 Quantum Sci. Technol. 8 025010](https://doi.org/10.1088/2058-9565/acb796). +166. “Noise-resistant quantum state compression readout”, Ding, C., Xu, XY., Niu, YF. et al , [Sci. China Phys. Mech. Astron. 66, 230311 (2023)](https://doi.org/10.1007/s11433-022-2005-x). +167. “Quantum Conformal Prediction for Reliable Uncertainty Quantification in Quantum Machine Learning”, S. Park and O. Simeone, [EEE Transactions on Quantum Engineering, vol. 5, pp. 1-24, 2024](https://doi.org/10.1109/TQE.2023.3333224). +168. “Variational preparation of entangled states on quantum computers”, Vu Tuan Hai, Nguyen Tan Viet, Le Bin Ho, [arXiv:2306.174226](https://doi.org/10.48550/arXiv.2306.17422). +169. “PyQBench: A Python library for benchmarking gate-based quantum computers”, Konrad Jałowiecki, Paulina Lewandowska, Łukasz Pawela, [SoftwareX 24, 101558 (2023)](https://doi.org/10.1016/j.softx.2023.101558). +170. “Mapping Topology-Localization Phase Diagram with Quasiperiodic Disorder Using a Programmable Superconducting Simulator”, Xuegang Li, Huikai Xu, Junhua Wang, Ling-Zhi Tang, Dan-Wei Zhang, Chuhong Yang, Tang Su, Chenlu Wang, Zhenyu Mi, Weijie Sun, Xuehui Liang, Mo Chen, Chengyao Li, Yingshan Zhang, Kehuan Linghu, Jiaxiu Han, Weiyang Liu, Yulong Feng, Pei Liu, Guangming Xue, Jingning Zhang, Yirong Jin, Shi-Liang Zhu, Haifeng Yu, S. P. Zhao, Qi-Kun Xue, [arXiv:2301.12138](https://doi.org/10.48550/arXiv.2301.12138). +171. “Universal framework for simultaneous tomography of quantum states and SPAM noise”, Abhijith Jayakumar, Stefano Chessa, Carleton Coffrin, Andrey Y. Lokhov, Marc Vuffray, Sidhant Misra, [Quantum 8, 1426 (2024)](https://doi.org/10.22331/q-2024-07-30-1426). +172. “Stochastic Approximation of Variational Quantum Imaginary Time Evolution”, Julien Gacon; Christa Zoufal; Giuseppe Carleo; Stefan Woerner, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://doi.org/10.1109/QCE57702.2023.10367741). +173. “Simulating Polaritonic Ground States on Noisy Quantum Devices”, Mohammad Hassan, Mohammad Hassan, Fabijan Pavošević, Derek S. Wang, Johannes Flick, [Phys. Chem. Lett. 2024, 15, 5, 1373](https://doi.org/10.1021/acs.jpclett.3c02875). +174. “High-fidelity realization of the AKLT state on a NISQ-era quantum processors”, Tianqi Chen, Ruizhe Shen, Ching Hua Lee, Bo Yang, [SciPost Phys. 15, 170 (2023)](https://doi.org/10.21468/SciPostPhys.15.4.170). +175. “Error mitigated quantum circuit cutting”, Ritajit Majumdar, Christopher J. Wood, [arXiv:2211.13431](https://doi.org/10.48550/arXiv.2211.13431). +176. “Quantum computation of π → π\* and n → π\* excited states of aromatic heterocycles”, Castellanos, M. A., Motta, M., & Rice, J. E., [Molecular Physics, 122(7–8) (2023)](https://doi.org/10.1080/00268976.2023.2282736). +177. “QuCT: A Framework for Analyzing Quantum Circuit by Extracting Contextual and Topological Features”, Tan, Siwei and Lang, Congliang and Xiang, Liang and Wang, Shudi and Jia, Xinghui and Tan, Ziqi and Li, Tingting and Yin, Jieming and Shang, Yongheng and Python, Andre and Lu, Liqiang and Yin, Jianwei, [Proceedings of the 56th Annual IEEE/ACM International Symposium on Microarchitecture (2023)](https://doi.org/10.1145/3613424.36142746). +178. “Fair Sampling Error Analysis on NISQ Devices”, Golden, John and Bartschi, Andreas and O’Malley, Daniel and Eidenbenz, Stephan, [ACM Transactions on Quantum Computing 3, 8 (2022)](https://doi.org/10.1145/3510857). +179. “Quantum circuits for discrete graphical models”, Piatkowski, N., Zoufal, C., [Quantum Mach. Intell. 6, 37 (2024)](https://doi.org/10.1007/s42484-024-00175-y). +180. “Benchmarking noisy intermediate scale quantum error mitigation strategies for ground state preparation of the HCl molecule”, Tim Weaving, Alexis Ralli, William M. Kirby, Peter J. Love, Sauro Succi, and Peter V. Coveney, [Phys. Rev. Research 5, 043054 (2024)](https://doi.org/10.1103/PhysRevResearch.5.043054). +181. “Braiding fractional quantum Hall quasiholes on a superconducting quantum processor”, Ammar Kirmani, Derek S. Wang, Pouyan Ghaemi, and Armin Rahmani, [Phys. Rev. B 108, 064303 (2023)](https://doi.org/10.1103/PhysRevB.108.064303). +182. “Mitigating Coupling Map Constrained Correlated Measurement Errors on Quantum Devices”, Robertson, Alan and Song, Shuaiwen, [Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis (2023)](https://doi.org/10.1145/3581784.3607039). +183. “Modular quantum circuits for secure communication”, Andrea Ceschini, Antonello Rosato, Massimo Panella, [IET Quantum Communication 4, 208 (2023)](https://doi.org/10.1049/qtc2.12065). +184. “Amplitude-based implementation of the unit step function on a quantum computer”, Jonas Koppe and Mark-Oliver Wolf, [Phys. Rev. A 107, 022606 (2023)](https://doi.org/10.1103/PhysRevA.107.022606). +185. “Perturbative readout-error mitigation for near-term quantum computers”, Evan Peters, Andy C. Y. Li, and Gabriel N. Perdue, [Phys. Rev. A 107, 062426 (2023)](https://doi.org/10.1103/PhysRevA.107.062426). +186. “Calculation of the moscovium ground-state energy by quantum algorithms”, V. A. Zaytsev, M. E. Groshev, I. A. Maltsev, A. V. Durova, V. M. Shabaev, [International Journal of Quantum Chemistry 124, e27232 (2023)](https://doi.org/10.1002/qua.272326). +187. “Scalable evaluation of incoherent infidelity in quantum devices”, Jader P. Santos, Ivan Henao, Raam Uzdin, [arXiv:2305.19359](https://doi.org/10.48550/arXiv.2305.19359). +188. “Application of the Variational Quantum Eigensolver to the Ultimate Pit Problem”, Yousef Hindy; Jessica Pointing; Meltem Tolunay; Sreeram Venkatarao; Mario Motta; Joseph A. Latone, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://doi.org/10.1109/QCE57702.2023.00083). +189. “Dynamical mean-field theory for the Hubbard-Holstein model on a quantum device”, Steffen Backes, Yuta Murakami, Shiro Sakai, and Ryotaro Arita, [Phys. Rev. B 107, 165155 (2023)](https://doi.org/10.1103/PhysRevB.107.165155). +190. “Identifying Bottlenecks of NISQ-friendly HHL algorithms”, Marc Andreu Marfany, Alona Sakhnenko, Jeanette Miriam Lorenz, [arXiv:2406.06288](https://doi.org/10.48550/arXiv.2406.06288). +191. “Dual-GSE: Resource-efficient Generalized Quantum Subspace Expansion”, Bo Yang, Nobuyuki Yoshioka, Hiroyuki Harada, Shigeo Hakkaku, Yuuki Tokunaga, Hideaki Hakoshima, Kaoru Yamamoto, Suguru Endo, [arXiv:2309.14171](https://doi.org/10.48550/arXiv.2309.14171). +192. “Energy Risk Analysis with Dynamic Amplitude Estimation and Piecewise Approximate Quantum Compiling”, K. Ghosh et al, [IEEE Transactions on Quantum Engineering](https://doi.org/10.1109/TQE.2024.3425969). +193. “QuFEM: Fast and Accurate Quantum Readout Calibration Using the Finite Element Method”, Tan, Siwei and Lu, Liqiang and Zhang, Hanyu and Yu, Jia and Lang, Congliang and Shang, Yongheng and Zhao, Xinkui and Chen, Mingshuai and Liang, Yun and Yin, Jianwei, [ASPLOS ‘24: Proceedings of the 29th ACM International Conference on Architectural Support for Programming Languages and Operating Systems](https://doi.org/10.1145/3620665.3640380). +194. “Quantum Risk Analysis: Beyond (Conditional) Value-at-Risk”, Christian Laudagé, Ivica Turkalj, [arXiv:2211.04456](https://doi.org/10.48550/arXiv.2211.04456). +195. “Correlation thresholds for effective composite pulse quantum error mitigation”, Ido Kaplan, Haim Suchowski, Yaron Oz, [arXiv:2308.08691](https://doi.org/10.48550/arXiv.2308.08691). +196. “Robust design under uncertainty in quantum error mitigation”, Piotr Czarnik, Michael McKerns, Andrew T. Sornborger, Lukasz Cincio, [arXiv:2307.05302](https://doi.org/10.48550/arXiv.2307.05302). +197. “Qubit Assignment Using Time Reversal”, Evan Peters, Prasanth Shyamsundar, Andy C.Y. Li, and Gabriel Perdue, [PRX Quantum 3, 040333 (2022)](https://doi.org/10.1103/PRXQuantum.3.040333). +198. “Quantum Simulations for Carbon Capture on Metal-Organic Frameworks”, Gopal Ramesh Dahale, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE),](https://doi.org/10.1109/QCE57702.2023.10189). +199. “Measuring qubit stability in a gate-based NISQ hardware processor”, Yeter-Aydeniz, K., Parks, Z., Thekkiniyedath, A.N. et al., [Quantum Inf Process 22, 96 (2023),](https://doi.org/10.1007/s11128-023-03826-4). +200. “Self-consistent quantum measurement tomography based on semidefinite programming”, Marco Cattaneo, Matteo A. C. Rossi, Keijo Korhonen, Elsi-Mari Borrelli, Guillermo García-Pérez, Zoltán Zimborás, and Daniel Cavalcanti, [Phys. Rev. Research 5, 033154 (2023),](https://doi.org/10.1103/PhysRevResearch.5.033154). +201. “Adaptive variational simulation for open quantum systems”, Huo Chen, Niladri Gomes, Siyuan Niu, Wibe Albert de Jong, [Quantum 8, 1252 (2024),](https://doi.org/10.22331/q-2024-02-13-1252). +202. “Conditions for a quadratic quantum speedup in nonlinear transforms with applications to energy contract pricing”, Gabriele Agliardi, Corey O’Meara, Kavitha Yogaraj, Kumar Ghosh, Piergiacomo Sabino, Marina Fernández-Campoamor, Giorgio Cortiana, Juan Bernabé-Moreno, Francesco Tacchino, Antonio Mezzacapo, Omar Shehab, [arXiv:2304.10385](https://doi.org/10.48550/arXiv.2304.10385). +203. “Error estimation in current noisy quantum computers”, Aseguinolaza, U., Sobrino, N., Sobrino, G. et al, [Quantum Inf Process 23, 181 (2024),](https://doi.org/10.1007/s11128-024-04384-z). +204. “Probing The Unitarity of Quantum Evolution Through Periodic Driving”, Alaina M. Green, Tanmoy Pandit, C. Huerta Alderete, Norbert M. Linke, Raam Uzdin, [arXiv:2212.10771](https://doi.org/10.48550/arXiv.2212.10771). +205. “Universal compilation for quantum state preparation and tomography”, Vu Tuan Hai, Le Bin Ho, [arXiv:2204.11635](https://doi.org/10.48550/arXiv.2204.11635). +206. “Folding-Free ZNE: A Comprehensive Quantum Zero-Noise Extrapolation Approach for Mitigating Depolarizing and Decoherence Noise”, Hrushikesh Pramod Patil; Peiyi Li; Ji Liu; Huiyang Zhou, [2023 IEEE International Conference on Quantum Computing and Engineering (QCE),](https://doi.org/10.1109/QCE57702.2023.00104). +207. “Testing the necessity of complex numbers in quantum mechanics with IBM quantum computers”, Jarrett L. Lancaster, Nicholas M. Palladino, [arXiv:2205.01262](https://doi.org/10.48550/arXiv.2205.01262). + diff --git a/docs/addons/qiskit-addon-mthree/probs.mdx b/docs/addons/qiskit-addon-mthree/probs.mdx new file mode 100644 index 00000000000..4aaef4cbc76 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/probs.mdx @@ -0,0 +1,64 @@ +--- +title: "Converting to probabilities" +description: "Converting to probabilities for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Converting to probabilities + +M3 natively works with quasi-probability distributions; distributions that contain negative values but nonetheless sum to one. This is useful for mitigating expectation values, but there could be situations where a true probability distribution is useful / needed. To this end, it is possible to find the closest probability distribution to a quasi-probability distribution in terms of $L2$-norm using: [`mthree.classes.QuasiDistribution.nearest_probability_distribution()`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution#mthree.classes.QuasiDistribution.nearest_probability_distribution "mthree.classes.QuasiDistribution.nearest_probability_distribution"). This conversion is done in linear time. + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(6) +qc.reset(range(6)) +qc.h(3) +qc.cx(3,1) +qc.cx(3,5) +qc.cx(1,0) +qc.cx(5,4) +qc.cx(1,2) +qc.measure_all() + +backend = FakeCasablancaV2() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(range(6)) + +trans_qc = transpile(qc, backend) +raw_counts = backend.run(trans_qc, shots=8192).result().get_counts() + +quasis = mit.apply_correction(raw_counts, range(6)) + +# Here is where the conversion happens +quasis.nearest_probability_distribution() +``` + +``` +{'010111': np.float32(1.8458784e-05), + '001101': np.float32(7.82907e-05), + '101011': np.float32(0.0001266693), + '101000': np.float32(0.00019029173), + '101101': np.float32(0.00033072094), + '000010': np.float32(0.00038816765), + '000110': np.float32(0.0006664434), + '110011': np.float32(0.0007018661), + '101111': np.float32(0.00095244986), + '001000': np.float32(0.0015221569), + '110111': np.float32(0.0019342253), + '110000': np.float32(0.0025582286), + '111001': np.float32(0.0028622507), + '010000': np.float32(0.003231837), + '001111': np.float32(0.0034692101), + '100000': np.float32(0.0035603144), + '111101': np.float32(0.0043579647), + '111000': np.float32(0.005170968), + '000111': np.float32(0.007296363), + '111011': np.float32(0.017431116), + '111111': np.float32(0.4505793), + '000000': np.float32(0.49257272)} +``` + diff --git a/docs/addons/qiskit-addon-mthree/runtime.mdx b/docs/addons/qiskit-addon-mthree/runtime.mdx new file mode 100644 index 00000000000..f13ccc71824 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/runtime.mdx @@ -0,0 +1,25 @@ +--- +title: "Using Runtime execution modes" +description: "Using Runtime execution modes for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Using Runtime execution modes + +The Qiskit Runtime has two execution modes, Batch and Session, that allow for grouping multiple jobs. You can include M3 calibration jobs in these modes using the runtime\_mode argument in mthree.M3Mitigation.cals\_from\_system. For example: + +```python +from qiskit_ibm_runtime import Batch +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +backend = FakeCasablancaV2() +batch = Batch(backend=backend) + +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(runtime_mode=batch); # This is where the Batch or Session goes +``` + +Note that if no runtime\_mode is set, and the passed system is an IBM backend, then jobs are submitted in Job mode, i.e. independently, by default. + diff --git a/docs/addons/qiskit-addon-mthree/sampling.ipynb b/docs/addons/qiskit-addon-mthree/sampling.ipynb new file mode 100644 index 00000000000..2ea56829c8c --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/sampling.ipynb @@ -0,0 +1,402 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Mitigating sampling problems\"\n", + "description: \"Mitigating sampling problems for the latest version of Matrix-free Measurement Mitigation (M3)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "43f3f626", + "metadata": {}, + "source": [ + "# Mitigating sampling problems\n", + "\n", + "There are many cases where one is interested in computing the most probable bitstring in the output distribution from a circuit. Although, M3 returns quasi-probabilities, the most likely bit-string is something that is always positive, and we can mitigate sampling problems of this form. Below is a simple example for Bernstein–Vazirani (BV) circuits." + ] + }, + { + "cell_type": "markdown", + "id": "faf78437", + "metadata": {}, + "source": [ + "## Frontmatter" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "0d41ee42", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:27.975572Z", + "iopub.status.busy": "2026-05-22T15:06:27.975341Z", + "iopub.status.idle": "2026-05-22T15:06:28.853671Z", + "shell.execute_reply": "2026-05-22T15:06:28.853381Z" + } + }, + "outputs": [], + "source": [ + "from qiskit import *\n", + "from qiskit_ibm_runtime.fake_provider import FakeKolkataV2\n", + "\n", + "import mthree\n", + "\n", + "import matplotlib.pyplot as plt\n", + "plt.style.use('quantum-light')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ee597ad5", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:28.855507Z", + "iopub.status.busy": "2026-05-22T15:06:28.855319Z", + "iopub.status.idle": "2026-05-22T15:06:28.876282Z", + "shell.execute_reply": "2026-05-22T15:06:28.876004Z" + } + }, + "outputs": [], + "source": [ + "backend = FakeKolkataV2()" + ] + }, + { + "cell_type": "markdown", + "id": "eafbbf6f", + "metadata": {}, + "source": [ + "## Setup experiment\n", + "\n", + "Here we generate BV circuits for all-ones bit-strings of various lengths, and transpile them against our target backend. We also create a (not-rigourous) function that finds the most likely bit-string from a distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8765752b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:28.877990Z", + "iopub.status.busy": "2026-05-22T15:06:28.877901Z", + "iopub.status.idle": "2026-05-22T15:06:28.881393Z", + "shell.execute_reply": "2026-05-22T15:06:28.881158Z" + } + }, + "outputs": [], + "source": [ + "def bv_ones_circs(N):\n", + " \"\"\"Create a Bernstein–Vazirani (BV) circuit of width N\n", + "\n", + " Here the bit-string length is N-1\n", + "\n", + " Parameters:\n", + " N (int): Number of qubits in circuit\n", + "\n", + " Returns:\n", + " QuantumCircuit: BV circuit\n", + " \"\"\"\n", + " qc = QuantumCircuit(N, N-1)\n", + " qc.x(N-1)\n", + " qc.h(range(N))\n", + " qc.cx(range(N-1), N-1)\n", + " qc.h(range(N-1))\n", + " qc.barrier()\n", + " qc.measure(range(N-1), range(N-1))\n", + " return qc\n", + "\n", + "\n", + "def most_likely_bitstring(dist):\n", + " \"\"\"Computes the most likely bit-string from a given distribution\n", + "\n", + " Parameters:\n", + " dist (dict): Input distribution\n", + "\n", + " Returns:\n", + " str: Most likely bit-string\n", + " int, float: Most likely value\n", + " \"\"\"\n", + " sorted_dist = dict(sorted(dist.items(), key=lambda item: item[1], reverse=True))\n", + " key = next(iter(sorted_dist.keys()))\n", + " val = next(iter(sorted_dist.values()))\n", + " return key, val" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6dae48a2", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:28.882625Z", + "iopub.status.busy": "2026-05-22T15:06:28.882541Z", + "iopub.status.idle": "2026-05-22T15:06:28.885329Z", + "shell.execute_reply": "2026-05-22T15:06:28.885078Z" + } + }, + "outputs": [], + "source": [ + "bit_range = range(2, 13)\n", + "\n", + "circs = [bv_ones_circs(N+1) for N in bit_range]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3fa878bc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:28.886636Z", + "iopub.status.busy": "2026-05-22T15:06:28.886556Z", + "iopub.status.idle": "2026-05-22T15:06:28.947845Z", + "shell.execute_reply": "2026-05-22T15:06:28.947596Z" + } + }, + "outputs": [], + "source": [ + "trans_circs = transpile(circs, backend, optimization_level=3)" + ] + }, + { + "cell_type": "markdown", + "id": "b4c3ffb3", + "metadata": {}, + "source": [ + "## Perform experiment\n", + "\n", + "We generate the raw data for the circuits, and then perform our mitigation in the usual way." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "45a3d55d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:28.949373Z", + "iopub.status.busy": "2026-05-22T15:06:28.949293Z", + "iopub.status.idle": "2026-05-22T15:06:40.643782Z", + "shell.execute_reply": "2026-05-22T15:06:40.643415Z" + } + }, + "outputs": [], + "source": [ + "shots = 8192\n", + "counts = backend.run(trans_circs, shots=shots).result().get_counts()" + ] + }, + { + "cell_type": "markdown", + "id": "07c46949", + "metadata": {}, + "source": [ + "Get the success probability, validating that our returned bit-string is what we expect." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "58a21b32", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.646018Z", + "iopub.status.busy": "2026-05-22T15:06:40.645917Z", + "iopub.status.idle": "2026-05-22T15:06:40.648505Z", + "shell.execute_reply": "2026-05-22T15:06:40.648069Z" + } + }, + "outputs": [], + "source": [ + "raw_success_probs = []\n", + "\n", + "for idx, num_bits in enumerate(bit_range):\n", + " max_bitstring, max_value = most_likely_bitstring(counts[idx])\n", + " if max_bitstring != '1'*num_bits:\n", + " raise Exception('Returned wrong bit-string')\n", + " raw_success_probs.append(max_value/shots)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fe41dbda", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.650173Z", + "iopub.status.busy": "2026-05-22T15:06:40.650072Z", + "iopub.status.idle": "2026-05-22T15:06:40.653956Z", + "shell.execute_reply": "2026-05-22T15:06:40.653621Z" + } + }, + "outputs": [], + "source": [ + "mit = mthree.M3Mitigation(backend)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e8a48d65", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.655448Z", + "iopub.status.busy": "2026-05-22T15:06:40.655379Z", + "iopub.status.idle": "2026-05-22T15:06:40.657794Z", + "shell.execute_reply": "2026-05-22T15:06:40.657465Z" + } + }, + "outputs": [], + "source": [ + "mappings = mthree.utils.final_measurement_mapping(trans_circs)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "34c20334", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.659684Z", + "iopub.status.busy": "2026-05-22T15:06:40.659596Z", + "iopub.status.idle": "2026-05-22T15:06:40.913805Z", + "shell.execute_reply": "2026-05-22T15:06:40.913488Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mit.cals_from_system(mappings)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3092a76a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.915482Z", + "iopub.status.busy": "2026-05-22T15:06:40.915385Z", + "iopub.status.idle": "2026-05-22T15:06:40.923491Z", + "shell.execute_reply": "2026-05-22T15:06:40.923141Z" + } + }, + "outputs": [], + "source": [ + "quasis = mit.apply_correction(counts, mappings)" + ] + }, + { + "cell_type": "markdown", + "id": "409f4771", + "metadata": {}, + "source": [ + "Get the mitigated success probability, validating that our returned bit-string is what we expect." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c547dd1e", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.925619Z", + "iopub.status.busy": "2026-05-22T15:06:40.925515Z", + "iopub.status.idle": "2026-05-22T15:06:40.927855Z", + "shell.execute_reply": "2026-05-22T15:06:40.927581Z" + } + }, + "outputs": [], + "source": [ + "mit_success_probs = []\n", + "\n", + "for idx, num_bits in enumerate(bit_range):\n", + " max_bitstring, max_value = most_likely_bitstring(quasis[idx])\n", + " if max_bitstring != '1'*num_bits:\n", + " raise Exception('Returned wrong bit-string')\n", + " mit_success_probs.append(max_value)" + ] + }, + { + "cell_type": "markdown", + "id": "d099e9e0", + "metadata": {}, + "source": [ + "## Plot results\n", + "\n", + "Here we plot the results" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "40654bec", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-22T15:06:40.929516Z", + "iopub.status.busy": "2026-05-22T15:06:40.929424Z", + "iopub.status.idle": "2026-05-22T15:06:41.009652Z", + "shell.execute_reply": "2026-05-22T15:06:41.009199Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.plot(bit_range, raw_success_probs, label='Raw')\n", + "ax.plot(bit_range, mit_success_probs, label='Mitigated')\n", + "ax.set_ylabel('Success probability')\n", + "ax.set_xlabel('Bit-string length')\n", + "ax.legend();" + ] + } + ], + "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-mthree/transpiled.mdx b/docs/addons/qiskit-addon-mthree/transpiled.mdx new file mode 100644 index 00000000000..3231721dfac --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/transpiled.mdx @@ -0,0 +1,62 @@ +--- +title: "Using M3 with transpiled circuits" +description: "Using M3 with transpiled circuits for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Using M3 with transpiled circuits + +In the [Basic usage](basic#basic) section we looked at circuits with a qubit layout that was known ahead of time. However, when taking an arbitrary circuit and compiling it down to hardware, SWAP mapping permutes qubits such that the final mapping between virtual circuit qubits and physical qubits is not a-priori known. For example consider the Bernstein-Vazirani circuit + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(5, 4) +qc.reset(range(5)) +qc.x(4) +qc.h(range(5)) +qc.cx(range(4), 4) +qc.draw() +qc.h(range(4)) +qc.barrier() +qc.measure(range(4), range(4)) +qc.draw('mpl') +``` + +![\_images/transpiled\_0\_0.png](/docs/images/addons/qiskit-addon-mthree/transpiled_0_0.avif) + +The target Casablanca system does not have the needed connectivity to natively embed the circuit and we must SWAP map it: + +```python +backend = FakeCasablancaV2() +trans_qc = transpile(qc, backend, optimization_level=3, seed_transpiler=12345) +trans_qc.draw('mpl') +``` + +![\_images/transpiled\_1\_0.png](/docs/images/addons/qiskit-addon-mthree/transpiled_1_0.avif) + +We can see from the measurements at the end that what was circuit qubit 0 is now mapped to physical qubit 5, circuit qubit 1 is mapped to physical qubit 3, etc… This information is needed to correctly mitigate the final results, yet outside of visually inspecting the circuit there is no easy way to obtain this information in Qiskit. As such, M3 includes the [`mthree.utils.final_measurement_mapping()`](/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping#mthree.utils.final_measurement_mapping "mthree.utils.final_measurement_mapping") routine to compute this for you: + +```python +mapping = mthree.utils.final_measurement_mapping(trans_qc) +print(mapping) +``` + +``` +{0: 1, 1: 3, 2: 4, 3: 6} +``` + +We see that the keys of the returned dictionary label the classical bits used, and the corresponding values show which qubit was measured into that bit. Using this mapping in M3 is easy: + +```python +mit = mthree.M3Mitigation(backend) +mit.cals_from_system(mapping) +``` + +``` +[] +``` + diff --git a/docs/addons/qiskit-addon-mthree/utils.mdx b/docs/addons/qiskit-addon-mthree/utils.mdx new file mode 100644 index 00000000000..98fbd4acc54 --- /dev/null +++ b/docs/addons/qiskit-addon-mthree/utils.mdx @@ -0,0 +1,137 @@ +--- +title: "Utility functions" +description: "Utility functions for the latest version of Matrix-free Measurement Mitigation (M3)" +--- + + + +# Utility functions + +There are a couple of functionalities that are needed for easy mitigation, but are not part of the Qiskit SDK and thus are included in M3. Each is detailed below. + +## Final-measurement mapping + +We have already seen the [`mthree.utils.final_measurement_mapping()`](/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping#mthree.utils.final_measurement_mapping "mthree.utils.final_measurement_mapping") routine in the section on detailing with [Transpiled circuits](transpiled#transpiled), where the addition of SWAP gates made it difficult to determine exactly which physical qubits are being used, and which classical bits they correspond to. Here we show another example of this usage. First we start with a 7-qubit GHZ state: + +```python +from qiskit import * +from qiskit_ibm_runtime.fake_provider import FakeCasablancaV2 +import mthree + +qc = QuantumCircuit(7) +qc.h(0) +qc.cx(0, range(1,7)) +qc.measure_all() +qc.draw('mpl') +``` + +![\_images/utils\_0\_0.png](/docs/images/addons/qiskit-addon-mthree/utils_0_0.avif) + +and then transpile it: + +```python +backend = FakeCasablancaV2() +trans_qc = transpile(qc, backend, optimization_level=3, seed_transpiler=54321) +trans_qc.draw('mpl') +``` + +![\_images/utils\_1\_0.png](/docs/images/addons/qiskit-addon-mthree/utils_1_0.avif) + +Once again we see that the physical qubits used and to which classical bits they map to is non-trivial to find. Yet this information is critical for successfully mitigating the results. This is where the [`mthree.utils.final_measurement_mapping()`](/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping#mthree.utils.final_measurement_mapping "mthree.utils.final_measurement_mapping") plays a critical role: + +```python +mapping = mthree.utils.final_measurement_mapping(trans_qc) +mapping +``` + +``` +{0: 5, 1: 0, 2: 1, 3: 2, 4: 4, 5: 3, 6: 6} +``` + +The keys of this mapping index the classical bits, whereas the values tell you which qubits were measured to obtain the bit values. The mapping is ordered in terms of the classical bit indices. You can just pass the generated `mapping` into M3 functions. + +## Evaluating raw counts data + +When mitigating results one often wants to know how much better the results are with mitigation compared to without. However, Qiskit does not have great support for computing things like expectation values. So M3 includes the generic functions [`mthree.utils.expval()`](/docs/api/qiskit-addon-mthree/mthree-utils-expval#mthree.utils.expval "mthree.utils.expval"), [`mthree.utils.stddev()`](/docs/api/qiskit-addon-mthree/mthree-utils-stddev#mthree.utils.stddev "mthree.utils.stddev"), and [`mthree.utils.expval_and_stddev()`](/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev#mthree.utils.expval_and_stddev "mthree.utils.expval_and_stddev") that operate on the native `Counts` objects in Qiskit. + +For example let us compare raw data versus the mitigated results in a simple case. + +```python +from qiskit_ibm_runtime.fake_provider import FakeAthensV2 +backend = FakeAthensV2() +qc = QuantumCircuit(4) +qc.h(2) +qc.cx(2, 1) +qc.cx(2, 3) +qc.cx(1, 0) +qc.measure_all() + +raw_counts = backend.run(qc).result().get_counts() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system() +mit_counts = mit.apply_correction(raw_counts, qubits=range(4), + return_mitigation_overhead=True) + +print('Raw counts expval', mthree.utils.expval(raw_counts)) +print('Mitigated expval', mit_counts.expval()) +``` + +``` +Raw counts expval 0.83203125 +Mitigated expval 0.9472091794013977 +``` + +We can also compare things like upper-bounds on the standard deviation: + +```python +print('Raw counts uncertainty', mthree.utils.stddev(raw_counts)) +print('Mitigated uncertainty', mit_counts.stddev()) +``` + +``` +Raw counts uncertainty 0.03125 +Mitigated uncertainty 0.038421723312172806 +``` + +where the uncertainty for the raw `Counts` data is just $1/\sqrt{\rm{shots}}$. + +These convenence functions work in the same manner as the methods for the distribution classes [`mthree.classes.QuasiDistribution`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution#mthree.classes.QuasiDistribution "mthree.classes.QuasiDistribution") and [`mthree.classes.ProbDistribution`](/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution#mthree.classes.ProbDistribution "mthree.classes.ProbDistribution") and collections [`mthree.classes.QuasiCollection`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection#mthree.classes.QuasiCollection "mthree.classes.QuasiCollection") and [`mthree.classes.ProbCollection`](/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection#mthree.classes.ProbCollection "mthree.classes.ProbCollection"). That is to say that, for example, I can pass operators to `expval` function: + +```python +print('These should be equal:', mthree.utils.expval(raw_counts, 'IIII'), + mit_counts.expval('IIII')) +``` + +``` +These should be equal: 1.0 1.0 +``` + +The routines also allow you to pass the native M3 distributions and collections. E.g. + +```python +print(mthree.utils.expval(mit_counts), mit_counts.expval()) +``` + +``` +0.9472092 0.9472091794013977 +``` + +Finally we note that you can pass multiple values at the same time. Here we run and mitigate 5 circuits: + +```python +raw_counts = backend.run([qc]*5).result().get_counts() +mit = mthree.M3Mitigation(backend) +mit.cals_from_system() +mit_counts = mit.apply_correction(raw_counts, qubits=range(4), + return_mitigation_overhead=True) + + +print('Raw counts expval', mthree.utils.expval(raw_counts)) +print('Mitigated expval', mit_counts.expval()) +``` + +``` +Raw counts expval [0.8300781 0.8066406 0.8691406 0.8574219 0.83984375] +Mitigated expval [0.93635666 0.91089356 0.9790215 0.97029066 0.94678086] +``` + diff --git a/docs/addons/qiskit-addon-obp/_toc.json b/docs/addons/qiskit-addon-obp/_toc.json new file mode 100644 index 00000000000..2f699ac7bff --- /dev/null +++ b/docs/addons/qiskit-addon-obp/_toc.json @@ -0,0 +1,63 @@ +{ + "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": "How-To Guides", + "children": [ + { + "title": "Using different Lp-norms for Pauli term truncation", + "url": "/docs/addons/qiskit-addon-obp/how_tos/bound-error-using-p-norm" + }, + { + "title": "Classically simulating circuits with OBP", + "url": "/docs/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp" + }, + { + "title": "Truncating Pauli terms during backpropagation", + "url": "/docs/addons/qiskit-addon-obp/how_tos/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": "/docs/tutorials/operator-back-propagation" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Operator backpropagation (OBP) API reference", + "url": "/docs/api/qiskit-addon-obp" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-obp/how_tos/bound-error-using-p-norm.ipynb b/docs/addons/qiskit-addon-obp/how_tos/bound-error-using-p-norm.ipynb new file mode 100644 index 00000000000..5bc702cd2e7 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/how_tos/bound-error-using-p-norm.ipynb @@ -0,0 +1,458 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Using different Lp-norms for Pauli term truncation\"\n", + "description: \"Using 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": [ + "# Using different Lp-norms for Pauli term truncation" + ] + }, + { + "cell_type": "markdown", + "id": "5958e635-64c1-4599-82ca-225b83312906", + "metadata": {}, + "source": [ + "**Note:** If you have not yet read the how-to guide on [truncating Pauli terms](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html) you should do so before reading this guide." + ] + }, + { + "cell_type": "markdown", + "id": "2780c587-cd2a-4398-8faa-de5e0c661a10", + "metadata": {}, + "source": [ + "You are already familiar with 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).\n", + "In this guide, you will learn about its additional 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": [ + "## 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": "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", + "# 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(hamiltonian, synthesis=LieTrotter(reps=3), time=2.0)\n", + "# slice the circuit by gate type\n", + "slices = slice_by_gate_types(circuit)\n", + "\n", + "# 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": "7a7c7299-0c82-4279-b3dc-4f39e8dddd7e", + "metadata": {}, + "source": [ + "The observable that we will look at is 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, we 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": [ + "## Using the L1 norm\n", + "\n", + "By default, and as you have already seen in the [other how-to guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/truncate_operator_terms.html), `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 rigorous but a very loose upper bound for most scenarios." + ] + }, + { + "cell_type": "markdown", + "id": "425293ce-49fb-4bd0-8d2f-44a3a43db967", + "metadata": {}, + "source": [ + "For this simple guide, we will backpropagate 6 slices of our example circuit.\n", + "We will use 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, slices[-max_slices:], truncation_error_budget=l1_truncation_error_budget\n", + ")\n", + "l1_reduced_circuit = combine_slices(slices[:-max_slices] + l1_remaining_slices)\n", + "print(f\"Backpropagated {max_slices - len(l1_remaining_slices)} circuit slices.\")\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 simply the accumulated sum of the slice errors. We can see clearly 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": [ + "## Using 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": [ + "Once again, we will backpropagate 6 slices of our 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, slices[-max_slices:], truncation_error_budget=l2_truncation_error_budget\n", + ")\n", + "l2_reduced_circuit = combine_slices(slices[:-max_slices] + l2_remaining_slices)\n", + "print(f\"Backpropagated {max_slices - len(l2_remaining_slices)} circuit slices.\")\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": [ + "Once again, we can compute the expectation value of the backpropagated observable, as well as 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 to 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 once again simply the sum of the single slice errors. This is yet 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/how_tos/index.mdx b/docs/addons/qiskit-addon-obp/how_tos/index.mdx new file mode 100644 index 00000000000..04fab7ff53f --- /dev/null +++ b/docs/addons/qiskit-addon-obp/how_tos/index.mdx @@ -0,0 +1,13 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Operator backpropagation (OBP)" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +* [Using different Lp-norms for Pauli term truncation](bound-error-using-p-norm) +* [Classically simulating circuits with OBP](simulating-circuits-with-obp) +* [Truncating Pauli terms during backpropagation](truncate-operator-terms) + diff --git a/docs/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp.ipynb b/docs/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp.ipynb new file mode 100644 index 00000000000..e3845339a56 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp.ipynb @@ -0,0 +1,548 @@ +{ + "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(hamiltonian, synthesis=LieTrotter(reps=3), time=2.0)\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 LayerError, PauliLindbladError\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", + " [(PauliLindbladErrorInstruction(layer_error_even_xx.error), slice_.qubits)]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_odd_xx.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [(PauliLindbladErrorInstruction(layer_error_odd_xx.error), slice_.qubits)]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_even_yy.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [(PauliLindbladErrorInstruction(layer_error_even_yy.error), slice_.qubits)]\n", + " )\n", + " )\n", + " elif slice_ == layer_error_odd_yy.circuit:\n", + " noisy_slices.append(\n", + " QuantumCircuit.from_instructions(\n", + " [(PauliLindbladErrorInstruction(layer_error_odd_yy.error), slice_.qubits)]\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(\"mpl\", fold=100, scale=0.8)" + ] + }, + { + "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[~vacuum_state_noisy_obs.paulis.x.any(axis=1)].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/how_tos/truncate-operator-terms.ipynb b/docs/addons/qiskit-addon-obp/how_tos/truncate-operator-terms.ipynb new file mode 100644 index 00000000000..9e8ae2fe6bf --- /dev/null +++ b/docs/addons/qiskit-addon-obp/how_tos/truncate-operator-terms.ipynb @@ -0,0 +1,924 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Truncating Pauli terms during backpropagation\"\n", + "description: \"Truncating Pauli terms during backpropagation for the latest version of Operator backpropagation (OBP)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "11cf5076-2777-499c-be50-531e305bf011", + "metadata": {}, + "source": [ + "# Truncating 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, please refer to [its how-to guide](https://qiskit.github.io/qiskit-addon-obp/how_tos/bound_error_using_p_norm.html)." + ] + }, + { + "cell_type": "markdown", + "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30", + "metadata": {}, + "source": [ + "In the following examples, we will get to know various ways of building an [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) via 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": [ + "## Constructing an example circuit\n", + "\n", + "For the purposes of this guide, we will use the following circuit slices:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "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", + "# we generate a linear chain of 10 qubits\n", + "linear_chain = rustworkx.generators.path_graph(10)\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(hamiltonian, synthesis=LieTrotter(reps=3), time=2.0)\n", + "# slice the circuit by gate type\n", + "slices = slice_by_gate_types(circuit)\n", + "\n", + "# 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": "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", + "As a first step towards understanding how this works, we look at the simplest case of a fixed truncation budget as specified by the user.\n", + "\n", + "The most straight-forward way to specify the truncation budget is via the `max_error_per_slice` argument.\n", + "In fact, this is what is being done in [this tutorial](https://qiskit.github.io/qiskit-addon-obp/tutorials/01_getting_started.ipynb). 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 us an implicit truncation error of at most `0.018`, if the entire 18 slices were to be 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, slices, operator_budget=op_budget, 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": [ + "Let's 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 ramps up 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 cross-over 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 in fact the very last slice, slice 2 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": [ + "## Specifying slice budget explicitly" + ] + }, + { + "cell_type": "markdown", + "id": "80f9337d-ea37-454c-ae0b-b101259568c9", + "metadata": {}, + "source": [ + "If one has knowledge of how to assign a budget to each slice, such that the backpropagation performance is optimized, they may want to explicitly assign a budget for each slice. For demonstration purposes, let's assign the first 3 slices zero budget and use the same budget 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(max_error_per_slice=max_error_per_slice)\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, slices, operator_budget=op_budget, 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 3 layers resulted in no terms being truncated until after the fourth layer, as can be confirmed in 3 of these 4 plots. What may be somewhat surprising is that although the 4th slice had no leftover budget passed to it, a term was still truncated 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 left-over budget curve flattens between slice ID's 3 and 4. Another notable detail is that at least one term was truncated from this point on, same as above.\n", + "\n", + "The key takeaway is that although we incurred less truncation error in this 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": [ + "## Specifying the budget cyclically" + ] + }, + { + "cell_type": "markdown", + "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9", + "metadata": {}, + "source": [ + "If one has a circuit with some repeatable pattern, such as a Trotter circuit, it may be desired 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 6 slices which are repeated 3 times for a total of 18 slices. Here 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", + "Once more pay attention to the fact that the slices are backpropagated in reverse order (i.e. 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": 9, + "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 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(max_error_per_slice=max_error_per_slice)\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, slices, operator_budget=op_budget, 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**, no truncation was performed over the first four slices since they were allocated no error budget. Slices 5 and 6 had some of their terms truncated as budget became available.\n", + "\n", + "As seen in the **top/left**, a relatively large amount of error was incurred after backpropagating slice 7, even though that slice was allocated no budget. This is because only about `.002` of the `.006` total budget allocated to slices 5 and 6 was used, so the remaining was forwarded to slice 7 and mostly expended, as seen in the **top/right**.\n", + "\n", + "**top/left** and **top/right** 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. **top/left**, **top/right**, and **bottom/left** 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 **bottom/right**.\n", + "\n", + "Another interesting thing 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 which could not be truncated until there was more budget after backpropagating slice 11, so they accumulated in the observable for several iterations. This particular 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": [ + "## Capping 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", + "Let's re-run the above experiment but set a cap on `max_error_total` such that the error budget is expended after backpropagating slice 7." + ] + }, + { + "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**). 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 **bottom/right**." + ] + }, + { + "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, slices, operator_budget=op_budget, 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": [ + "**top/right** shows how the entire `.018` error budget is made available to the first slice. No truncation is performed until the third slices, so the left-over 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": [ + "## Capping the number of backpropagated slices and the total error together\n", + "\n", + "If one doesn't want to distribute their error budget all the way through the circuit but doesn't want to greedily expend it either, one can specify a the number of slices they 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 may 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(max_error_total=0.018, num_slices=num_slices, p_norm=1)\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 (i.e. ``slices[-num_slices:]``) to ``backpropagate``, we must combine the slices remaining after backpropagation with the slices that were never sent for backpropagation (i.e. ``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). Here we inspect how many slices were backpropagated vs. 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 us 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 above 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": [ + "## Working 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": [ + "For simplicity, we repeat the very first experiment from this tutorial. The only difference is that we now have two observables to backpropagate the circuit into.\n", + "\n", + "For this particular example, this does not affect the number of slices which could be backpropagated. However, we can see now 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, slices, operator_budget=op_budget, 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 shed some light on how the backpropagation algorithm handles multiple observables.\n", + "\n", + "First, the plots **top/left**, **top/right** and **bottom/left** teach us 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 is not necessarily the case in general.\n", + "\n", + "**bottom/right** teaches us that `max_qwc_groups` is actually taking _all_ observables combined 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 (which we have 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..37f45ed19f6 --- /dev/null +++ b/docs/addons/qiskit-addon-obp/index.mdx @@ -0,0 +1,49 @@ +--- +title: "Qiskit addon: operator backpropagation (OBP)" +description: "Documentation for the latest version of Operator backpropagation (OBP)" +--- + +# Qiskit addon: operator backpropagation (OBP) + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +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 a number of ways in which operator backpropagation can be performed, this package uses a method based on Clifford perturbation theory, which has the benefit that the overhead incurred by backpropagating various 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 tame. + +## Documentation + +All documentation is available [here](https://qiskit.github.io/qiskit-addon-obp/). + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-obp' +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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 source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-obp). + +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://arxiv.org/abs/2502.01897), arXiv:2502.01897 \[quant-ph]. + +## 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..8a183e71a15 --- /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 + +Let’s see how to install the package. The first thing to do is 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) + +## Pre-Installation + +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 via `PyPI`. + +```sh +pip install 'qiskit-addon-obp' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may 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 in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-opt-mapper/_toc.json b/docs/addons/qiskit-addon-opt-mapper/_toc.json new file mode 100644 index 00000000000..93b38ea0416 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/_toc.json @@ -0,0 +1,61 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Optimization mapper 0.1", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-opt-mapper" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-opt-mapper/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "Migrate from qiskit-optimization to qiskit-addon-opt-mapper", + "url": "/docs/addons/qiskit-addon-opt-mapper/how_tos/00-migrate-from-qiskit-optimization" + }, + { + "title": "Model quantum optimization problems with the OptimizationProblem class", + "url": "/docs/addons/qiskit-addon-opt-mapper/how_tos/01-optimization-problem" + }, + { + "title": "Convert formulations of an OptimizationProblem", + "url": "/docs/addons/qiskit-addon-opt-mapper/how_tos/02-converters-for-optimization-problems" + }, + { + "title": "Validate your modeling with classical solvers", + "url": "/docs/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers" + }, + { + "title": "Translate between OptimizationProblem and Docplex", + "url": "/docs/addons/qiskit-addon-opt-mapper/how_tos/04-translate-docplex" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/qiskit-addon-opt-mapper" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Optimization mapper API reference", + "url": "/docs/api/qiskit-addon-opt-mapper" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-opt-mapper/how_tos/00-migrate-from-qiskit-optimization.ipynb b/docs/addons/qiskit-addon-opt-mapper/how_tos/00-migrate-from-qiskit-optimization.ipynb new file mode 100644 index 00000000000..61b32f02969 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/00-migrate-from-qiskit-optimization.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Migrate from qiskit-optimization to qiskit-addon-opt-mapper\"\n", + "description: \"Migrate from qiskit-optimization to qiskit-addon-opt-mapper for the latest version of Optimization mapper\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "898eb4ed", + "metadata": {}, + "source": [ + "# Migrate from `qiskit-optimization` to `qiskit-addon-opt-mapper`" + ] + }, + { + "cell_type": "markdown", + "id": "97e86ead", + "metadata": {}, + "source": [ + "This migration guide summarizes the steps required to migrate a workflow built using the `qiskit-optimization` [library](https://github.com/qiskit-community/qiskit-optimization) to the new and improved `qiskit-addon-opt-mapper` library." + ] + }, + { + "cell_type": "markdown", + "id": "2c4ecca4", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "The `qiskit-addon-opt-mapper` library shares a lot of common structure and design with `qiskit-optimization`, with a few key differences that should be taken into account when migrating from one package to the other:\n", + "\n", + "1. The core data structure of `qiskit-optimization` is the `QuadraticProgram`. In `qiskit-addon-opt-mapper`, this structure is now called `OptimizationProblem`. The core methods and attributes remain consistent with the `QuadraticProgram`, but the class is now extended to support higher-order terms in variables, objective functions, and constraints.\n", + "\n", + "2. The new `qiskit-addon-opt-mapper` package is focused on the optimization problem modeling and mapping step, which means there isn't a migration path for the following former `qiskit-optimization` modules: `algorithms`, `optimizers`, and `minimum_eigensolvers`.\n", + "\n", + "3. The subset of classical solvers from the `algorithms` module are now exposed in `qiskit-addon-opt-mapper` in a new `solvers` module." + ] + }, + { + "cell_type": "markdown", + "id": "eb297c29", + "metadata": {}, + "source": [ + "## Tables" + ] + }, + { + "cell_type": "markdown", + "id": "3390c217", + "metadata": {}, + "source": [ + "### General migration guidelines\n", + "\n", + "| `qiskit_optimization` | `qiskit_addon_opt_mapper` |\n", + "|-----------------------------------|-------------------------------------|\n", + "| `QuadraticProgram` | `OptimizationProblem` |\n", + "| any class that contains `QuadraticProgram`, for example, `QuadraticProgramConverter` | `OptimizationProblemConverter` |\n", + "| any `to_quadratic_program()` method | `to_optimization_problem()` |\n", + "| any `quadratic_program` input parameter or attribute | `optimization_problem` |" + ] + }, + { + "cell_type": "markdown", + "id": "4fa0f1a9", + "metadata": {}, + "source": [ + "### Migrate classical solvers\n", + "\n", + "| `qiskit_optimization.algorithms` | `qiskit_addon_opt_mapper.solvers` |\n", + "|-----------------------------------|-------------------------------------|\n", + "| `OptimizationAlgorithm` | `OptimizationSolver` |\n", + "| `OptimizationResult` | `SolverResult` |\n", + "| `CplexOptimizer` | `CplexSolver` |\n", + "| `GurobiOptimizer` | `GurobiSolver` |\n", + "| `ScipyMilpOptimizer` | `ScipyMilpSolver` |\n", + "| Any other class in this module | Not part of this package |" + ] + } + ], + "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-opt-mapper/how_tos/01-optimization-problem.ipynb b/docs/addons/qiskit-addon-opt-mapper/how_tos/01-optimization-problem.ipynb new file mode 100644 index 00000000000..70e94e86497 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/01-optimization-problem.ipynb @@ -0,0 +1,888 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Model quantum optimization problems with the OptimizationProblem class\"\n", + "description: \"Model quantum optimization problems with the OptimizationProblem class for the latest version of Optimization mapper\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "bf41b9ac-f020-484c-ab60-aa01ecaf4f00", + "metadata": {}, + "source": [ + "# Model quantum optimization problems with the `OptimizationProblem` class" + ] + }, + { + "cell_type": "markdown", + "id": "291527af-911f-40b9-92c2-9e6abc542ce5", + "metadata": {}, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "103163ec-23a2-4c19-a2de-1ecfd533b4f7", + "metadata": {}, + "source": [ + "This guide demonstrates how to use the **Optimization Mapper Qiskit addon** for quantum optimization modeling. The addon introduces the `OptimizationProblem` class, which provides a flexible way to handle variables, constraints, and objective functions.\n", + "\n", + "The `OptimizationProblem` class supports **higher-order polynomial objectives and constraints**, as well as **spin variables**, unlike the `QuadraticProgram` class from the legacy `qiskit-optimization`[package](https://github.com/qiskit-community/qiskit-optimization) objectives and constraints. With `OptimizationProblem`, you can work with a broader class of optimization models.\n", + "\n", + "`OptimizationProblem` handles **constrained polynomial programs**, which you can express in the following general form:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "\\text{minimize}\\quad& \\sum_{d=1}^{D} \\;\\; \\sum_{S \\subseteq \\{1,\\dots,n\\}, |S|=d} c_{S} \\prod_{i \\in S} x_i \\\\\n", + "\\text{subject to}\\quad&\n", + "\\sum_{d=1}^{D} \\;\\; \\sum_{S \\subseteq \\{1,\\dots,n\\}, |S|=d} a^{(j)}_{S} \\prod_{i \\in S} x_i \\;\\; \\leq b_j, \\quad j=1,\\dots,m \\\\\n", + "& l_i \\leq x_i \\leq u_i, \\quad i=1,\\dots,n,\n", + "\\end{align}\n", + "$$\n", + "\n", + "where the coefficients $c_S$ and $a^{(j)}_S$ can involve terms of any degree up to $D$.\n", + "The variables $x_i$ can be defined as binary, integer, continuous, or spin.\n", + "In addition to \"$\\leq$\" constraints, the `OptimizationProblem` model also supports \"$\\geq$\" and \"$=$\"." + ] + }, + { + "cell_type": "markdown", + "id": "e3875941-7ba0-47b1-acc5-d7e628011aa4", + "metadata": {}, + "source": [ + "## Construct an `OptimizationProblem`\n", + "\n", + "\n", + "This section explains how to build an optimization model by using the `OptimizationProblem` class. Start by creating an empty model. The model does not yet contain variables, an objective, or constraints. Note that `OptimizationProblem` has a `prettyprint` method to generate a comprehensive string representation of the model." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "14128e7a-10c7-4fd5-be9d-e5143cbd7700", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 0\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " No variables\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper import OptimizationProblem\n", + "\n", + "# make an empty problem\n", + "mod = OptimizationProblem(\"my problem\")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "55bab9cd-3ce8-4b49-804e-c948bd0c0cee", + "metadata": {}, + "source": [ + "The `OptimizationProblem` class supports four types of variables:\n", + "\n", + "- **Binary variables** can have the value 0 or 1.\n", + "- **Integer variables** can have any integer value within their bounds.\n", + "- **Continuous variables** can have any real value within their bounds.\n", + "- **Spin variables** can have the value -1 or 1.\n", + "\n", + "When you define a variable, you specify its name and type. You can also set optional lower and upper bounds." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b0076fb5-6b0a-4e7f-bfb7-085ff42a7da5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 0\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Add variables\n", + "mod.binary_var(name=\"x\")\n", + "mod.integer_var(name=\"y\", lowerbound=-1, upperbound=5)\n", + "mod.continuous_var(name=\"z\", lowerbound=-1, upperbound=5)\n", + "mod.spin_var(name=\"s\")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "e06126c8-0e42-4a7d-bb25-bc2b04c71a20", + "metadata": {}, + "source": [ + "You set the objective function by calling `OptimizationProblem.minimize` or `OptimizationProblem.maximize`.\n", + "\n", + "The objective function can include the following components:\n", + "\n", + "- A constant offset.\n", + "- Linear terms (\\(c^{\\top} x\\)).\n", + "- Quadratic terms (\\(x^{\\top} Q x\\)).\n", + "- Higher-order terms \\(\\sum_S h_S \\prod_{i \\in S} x_i\\), where \\(|S| \\ge 3\\).\n", + "\n", + "You can specify these terms by using a list, a matrix, or a dictionary.\n", + "\n", + "The following example shows how to define an objective function by using a dictionary:\n", + "\n", + "- For linear terms, the keys are variable names and the values are their coefficients.\n", + "- For quadratic terms, the keys are pairs of variables and the values are their coefficients.\n", + "- For higher-order terms, use a nested dictionary with the following structure: `Dict[int, Dict[Tuple[str | int, ...], float]]`, where the outer key is the degree of the term, and the inner dictionary maps a tuple of variable names (or indices) to its coefficient." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ce7544d7-4d80-4903-8f4e-8448256036d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Add objective function using dictionaries\n", + "mod.minimize(\n", + " constant=3,\n", + " linear={\"x\": 1},\n", + " quadratic={(\"x\", \"y\"): 2, (\"z\", \"z\"): -1},\n", + " higher_order={3: {(\"x\", \"y\", \"z\"): 4}},\n", + ")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "d932e5cd-6fd0-41c3-9e09-08f5d981f0c3", + "metadata": {}, + "source": [ + "You can also specify the objective function by using arrays:\n", + "\n", + "- For linear terms, use an array that corresponds to the vector $c$ in the mathematical formulation.\n", + "- For quadratic terms, use an array that corresponds to the matrix $Q$.\n", + "- For higher-order terms, use multi-dimensional arrays. An order-$k$ tensor represents the coefficients of degree-$k$ monomials. For example, a third-order tensor $H^{(3)}$ stores the coefficients $h_{ijk}$ for terms $x_i x_j x_k$." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e45ac176-fa21-4657-a95f-22d1def1d1b7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Add objective function using lists/arrays\n", + "# Here, h3 is a 3D array for the cubic terms\n", + "h3 = np.zeros((4, 4, 4)) # assuming we have 4 variables\n", + "h3[0, 1, 2] = 4 # corresponds to x * y * z\n", + "mod.minimize(\n", + " constant=3,\n", + " linear=[1, 0, 0, 0],\n", + " quadratic=[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, -1, 0], [0, 0, 0, 0]],\n", + " higher_order={3: h3},\n", + ")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "f954dfcf-f7bc-42de-a206-82920c45997e", + "metadata": {}, + "source": [ + "You can access the constant, the linear term, the quadratic term, and the higher-order terms by looking at\n", + "`OptimizationProblem.objective.{constant, linear, quadratic, higher_order}`, respectively.\n", + "\n", + "For linear, quadratic, and higher-order terms, you can obtain:\n", + "- a dense array (`to_array`),\n", + "- a sparse representation (`coefficients`),\n", + "- or a dictionary (`to_dict`).\n", + "\n", + "When using dictionaries, you can specify whether to use variable indices or names as keys.\n", + "\n", + "- **Quadratic terms** are stored in a compressed way. For example, `{('x', 'y'): 1, ('y', 'x'): 2}` is stored as `{('x', 'y'): 3}`.\n", + " You can get the quadratic term as a symmetric matrix by calling `to_array(symmetric=True)`.\n", + "\n", + "- **Higher-order terms** are represented as nested dictionaries:\n", + " `Dict[int, Dict[Tuple[str | int, ...], float]]`,\n", + " where the outer key is the degree, and the inner dictionary maps a tuple of variables to its coefficient.\n", + " For example, `{3: {('x','y','z'): 4}}` corresponds to the cubic term $4xyz$.\n", + " Similarly to quadratic terms, you can call `to_array(symmetric=True)` to aggregate coefficients across permutations of the same monomial." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "791beecf-1f2e-4432-8805-8c40fdec35f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "constant:\t\t\t 3.0\n", + "linear dict:\t\t\t {np.int32(0): np.int64(1)}\n", + "linear array:\t\t\t [1 0 0 0]\n", + "linear array as sparse matrix:\n", + " (np.int32(0), np.int32(0))\t1 \n", + "\n", + "quadratic dict w/ index:\t {(0, 1): np.float64(2.0), (2, 2): np.float64(-1.0)}\n", + "quadratic dict w/ name:\t\t {('x', 'y'): np.float64(2.0), ('z', 'z'): np.float64(-1.0)}\n", + "symmetric quadratic dict w/ name:\t {('x', 'y'): np.float64(1.0), ('y', 'x'): np.float64(1.0), ('z', 'z'): np.float64(-1.0)}\n", + "quadratic matrix:\n", + " [[ 0. 2. 0. 0.]\n", + " [ 0. 0. 0. 0.]\n", + " [ 0. 0. -1. 0.]\n", + " [ 0. 0. 0. 0.]] \n", + "\n", + "symmetric quadratic matrix:\n", + " [[ 0. 1. 0. 0.]\n", + " [ 1. 0. 0. 0.]\n", + " [ 0. 0. -1. 0.]\n", + " [ 0. 0. 0. 0.]] \n", + "\n", + "quadratic matrix as sparse matrix:\n", + " (np.int32(0), np.int32(1))\t2.0\n", + " (np.int32(2), np.int32(2))\t-1.0\n", + "higher-order dict w/ index:\t {(0, 1, 2): 4.0}\n", + "higher-order dict w/ name:\t {('x', 'y', 'z'): 4.0}\n", + "higher-order matrix:\n", + " [[[0. 0. 0. 0. ]\n", + " [0. 0. 0.66666667 0. ]\n", + " [0. 0.66666667 0. 0. ]\n", + " [0. 0. 0. 0. ]]\n", + "\n", + " [[0. 0. 0.66666667 0. ]\n", + " [0. 0. 0. 0. ]\n", + " [0.66666667 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]]\n", + "\n", + " [[0. 0.66666667 0. 0. ]\n", + " [0.66666667 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]]\n", + "\n", + " [[0. 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]\n", + " [0. 0. 0. 0. ]]] \n", + "\n", + "symmetric higher-order matrix:\n", + " [[[0. 0. 0. 0.]\n", + " [0. 0. 4. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "\n", + " [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "\n", + " [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "\n", + " [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]] \n", + "\n", + "higher-order matrix as sparse matrix:\n", + " {(0, 1, 2): 4.0}\n" + ] + } + ], + "source": [ + "print(\"constant:\\t\\t\\t\", mod.objective.constant)\n", + "print(\"linear dict:\\t\\t\\t\", mod.objective.linear.to_dict())\n", + "print(\"linear array:\\t\\t\\t\", mod.objective.linear.to_array())\n", + "print(\"linear array as sparse matrix:\\n\", mod.objective.linear.coefficients, \"\\n\")\n", + "print(\"quadratic dict w/ index:\\t\", mod.objective.quadratic.to_dict())\n", + "print(\"quadratic dict w/ name:\\t\\t\", mod.objective.quadratic.to_dict(use_name=True))\n", + "print(\n", + " \"symmetric quadratic dict w/ name:\\t\",\n", + " mod.objective.quadratic.to_dict(use_name=True, symmetric=True),\n", + ")\n", + "print(\"quadratic matrix:\\n\", mod.objective.quadratic.to_array(), \"\\n\")\n", + "print(\"symmetric quadratic matrix:\\n\", mod.objective.quadratic.to_array(symmetric=True), \"\\n\")\n", + "print(\"quadratic matrix as sparse matrix:\\n\", mod.objective.quadratic.coefficients)\n", + "print(\"higher-order dict w/ index:\\t\", mod.objective.higher_order[3].to_dict())\n", + "print(\"higher-order dict w/ name:\\t\", mod.objective.higher_order[3].to_dict(use_name=True))\n", + "print(\"higher-order matrix:\\n\", mod.objective.higher_order[3].to_array(), \"\\n\")\n", + "print(\n", + " \"symmetric higher-order matrix:\\n\", mod.objective.higher_order[3].to_array(symmetric=True), \"\\n\"\n", + ")\n", + "print(\"higher-order matrix as sparse matrix:\\n\", mod.objective.higher_order[3].coefficients)" + ] + }, + { + "cell_type": "markdown", + "id": "9ffda951-ad26-4d92-92f9-6dc26f233302", + "metadata": {}, + "source": [ + "## Add or remove linear, quadratic, and higher-order terms to and from the constraints" + ] + }, + { + "cell_type": "markdown", + "id": "4f793e66-c20f-40d0-b53e-b6b259a44291", + "metadata": {}, + "source": [ + "You can add linear constraints by setting name, linear expression, sense, and right-hand-side value (rhs).\n", + "You can use senses `'=='`, `'<='`, and `'>='`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7e76c822-b874-4dc2-b4f7-85ce23666cb3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " Linear constraints (3)\n", + " x + 2*y == 3 'lin_eq'\n", + " x + 2*y <= 3 'lin_leq'\n", + " x + 2*y >= 3 'lin_geq'\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Add linear constraints\n", + "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\"==\", rhs=3, name=\"lin_eq\")\n", + "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\"<=\", rhs=3, name=\"lin_leq\")\n", + "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\">=\", rhs=3, name=\"lin_geq\")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "59de667a-d5e4-4044-b518-e5933465fb6f", + "metadata": {}, + "source": [ + "You can add quadratic constraints by setting name, quadratic expression, sense, and right-hand-side value (rhs)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "935f8deb-ab94-466d-a2e2-e658cda1c744", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " Linear constraints (3)\n", + " x + 2*y == 3 'lin_eq'\n", + " x + 2*y <= 3 'lin_leq'\n", + " x + 2*y >= 3 'lin_geq'\n", + "\n", + " Quadratic constraints (3)\n", + " x^2 - y*z + x + y == 1 'quad_eq'\n", + " x^2 - y*z + x + y <= 1 'quad_leq'\n", + " x^2 - y*z + x + y >= 1 'quad_geq'\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Add quadratic constraints\n", + "mod.quadratic_constraint(\n", + " linear={\"x\": 1, \"y\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " sense=\"==\",\n", + " rhs=1,\n", + " name=\"quad_eq\",\n", + ")\n", + "mod.quadratic_constraint(\n", + " linear={\"x\": 1, \"y\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " sense=\"<=\",\n", + " rhs=1,\n", + " name=\"quad_leq\",\n", + ")\n", + "mod.quadratic_constraint(\n", + " linear={\"x\": 1, \"y\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " sense=\">=\",\n", + " rhs=1,\n", + " name=\"quad_geq\",\n", + ")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "a3886db2-3a14-4180-af3d-020136344ce8", + "metadata": {}, + "source": [ + "You can also add higher-order constraints by setting name, higher-order expression, sense, and right-hand-side value (rhs)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "35b9310c-2545-4734-aada-584d8db6e47a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " Linear constraints (3)\n", + " x + 2*y == 3 'lin_eq'\n", + " x + 2*y <= 3 'lin_leq'\n", + " x + 2*y >= 3 'lin_geq'\n", + "\n", + " Quadratic constraints (3)\n", + " x^2 - y*z + x + y == 1 'quad_eq'\n", + " x^2 - y*z + x + y <= 1 'quad_leq'\n", + " x^2 - y*z + x + y >= 1 'quad_geq'\n", + "\n", + " Higher-order constraints (3)\n", + " 2*s*x*y*z + x*y*z + x^2 - y*z + x == 1 'hoc_eq'\n", + " 2*s*x*y*z + x*y*z + x^2 - y*z + x <= 1 'hoc_leq'\n", + " 2*s*x*y*z + x*y*z + x^2 - y*z + x >= 1 'hoc_geq'\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Add higher-order constraints\n", + "mod.higher_order_constraint(\n", + " linear={\"x\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", + " sense=\"==\",\n", + " rhs=1,\n", + " name=\"hoc_eq\",\n", + ")\n", + "mod.higher_order_constraint(\n", + " linear={\"x\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", + " sense=\"<=\",\n", + " rhs=1,\n", + " name=\"hoc_leq\",\n", + ")\n", + "mod.higher_order_constraint(\n", + " linear={\"x\": 1},\n", + " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", + " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", + " sense=\">=\",\n", + " rhs=1,\n", + " name=\"hoc_geq\",\n", + ")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "5694b1ed-55f7-4799-9456-4a3072e5b1e2", + "metadata": {}, + "source": [ + "You can access linear and quadratic terms of linear and quadratic constraints in the same way as the objective function." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7d4d05ec-8a8c-482d-ad53-e32409816fb5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lin_geq: {'x': np.float64(1.0), 'y': np.float64(2.0)} ConstraintSense.GE 3\n", + "quad_geq: {'x': np.float64(1.0), 'y': np.float64(1.0)} {('x', 'x'): np.float64(1.0), ('y', 'z'): np.float64(-1.0)} ConstraintSense.GE 3\n", + "hoc_geq: {'x': np.float64(1.0)} {('x', 'x'): np.float64(1.0), ('y', 'z'): np.float64(-1.0)} {3: {('x', 'y', 'z'): 1.0}, 4: {('x', 'y', 'z', 's'): 2.0}} ConstraintSense.GE 3\n" + ] + } + ], + "source": [ + "lin_geq = mod.get_linear_constraint(\"lin_geq\")\n", + "print(\"lin_geq:\", lin_geq.linear.to_dict(use_name=True), lin_geq.sense, lin_geq.rhs)\n", + "quad_geq = mod.get_quadratic_constraint(\"quad_geq\")\n", + "print(\n", + " \"quad_geq:\",\n", + " quad_geq.linear.to_dict(use_name=True),\n", + " quad_geq.quadratic.to_dict(use_name=True),\n", + " quad_geq.sense,\n", + " lin_geq.rhs,\n", + ")\n", + "hoc_geq = mod.get_higher_order_constraint(\"hoc_geq\")\n", + "print(\n", + " \"hoc_geq:\",\n", + " hoc_geq.linear.to_dict(use_name=True),\n", + " hoc_geq.quadratic.to_dict(use_name=True),\n", + " {k: v.to_dict(use_name=True) for k, v in hoc_geq.higher_order.items()},\n", + " hoc_geq.sense,\n", + " lin_geq.rhs,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3dbb362f-4b96-4b0f-8454-2d9bd7eff171", + "metadata": {}, + "source": [ + "You can also remove linear/quadratic/higher-order constraints by using the `remove_linear_constraint`, `remove_quadratic_constraint`, and `remove_higher_order_constraint` methods, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "fe2a694f-852e-4f15-a676-7235830c8174", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " 4*x*y*z + 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " Linear constraints (2)\n", + " x + 2*y <= 3 'lin_leq'\n", + " x + 2*y >= 3 'lin_geq'\n", + "\n", + " Quadratic constraints (2)\n", + " x^2 - y*z + x + y == 1 'quad_eq'\n", + " x^2 - y*z + x + y >= 1 'quad_geq'\n", + "\n", + " Higher-order constraints (2)\n", + " 2*s*x*y*z + x*y*z + x^2 - y*z + x <= 1 'hoc_leq'\n", + " 2*s*x*y*z + x*y*z + x^2 - y*z + x >= 1 'hoc_geq'\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "# Remove constraints\n", + "mod.remove_linear_constraint(\"lin_eq\")\n", + "mod.remove_quadratic_constraint(\"quad_leq\")\n", + "mod.remove_higher_order_constraint(\"hoc_eq\")\n", + "print(mod.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "d87f7732-6d5e-4555-b688-5c575cc6134a", + "metadata": {}, + "source": [ + "## Substitute variables" + ] + }, + { + "cell_type": "markdown", + "id": "19971881-d0d9-4a37-9f4f-dd5a56a99400", + "metadata": {}, + "source": [ + "You can substitute some of the variables with constants or other variables.\n", + "More precisely, `OptimizationProblem` has a `substitute_variables(constants=..., variables=...)` method to deal with the following two cases.\n", + "\n", + "- $x \\leftarrow c$: when `constants` have a dictionary `{x: c}`.\n", + "- $x \\leftarrow c y$: when `variables` have a dictionary `{x: (y, c)}`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "a08b2b2d-364c-4f74-9a54-c94001670bd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my problem\n", + "\n", + "Minimize\n", + " -5*z^2 - 2*z + 4\n", + "\n", + "Subject to\n", + " Linear constraints (2)\n", + " -2*z <= 2 'lin_leq'\n", + " -2*z >= 2 'lin_geq'\n", + "\n", + " Quadratic constraints (2)\n", + " z^2 - z == -1 'quad_eq'\n", + " z^2 - z >= -1 'quad_geq'\n", + "\n", + " Higher-order constraints (2)\n", + " -2*s*z^2 <= -1 'hoc_leq'\n", + " -2*s*z^2 >= -1 'hoc_geq'\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 1\n", + "\n", + " Spin variables (1)\n", + " -1 <= s <= 1\n", + "\n" + ] + } + ], + "source": [ + "sub = mod.substitute_variables(constants={\"x\": 1}, variables={\"y\": (\"z\", -1)})\n", + "print(sub.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "5521704d-fcc3-4a0c-820e-1d1c99cf9937", + "metadata": {}, + "source": [ + "If the resulting problem is infeasible due to lower bounds or upper bounds, the methods returns the status `Status.INFEASIBLE`.\n", + "For example, if with the model above you try to replace variable `x` with -1, but -1 is out of range of `x` (0 <= `x` <= 1), it will return `Status.INFEASIBLE`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "acb951c1-3517-4bf8-9ebc-149468193171", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Infeasible substitution for variable: x\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OptimizationProblemStatus.INFEASIBLE\n" + ] + } + ], + "source": [ + "sub = mod.substitute_variables(constants={\"x\": -1})\n", + "print(sub.status)" + ] + }, + { + "cell_type": "markdown", + "id": "71955e4c-dfb9-4a41-b871-cdef2d7c2a44", + "metadata": {}, + "source": [ + "You cannot substitute variables multiple times.\n", + "The method raises an error in such cases." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d765e1c5-763b-4a61-a28c-337c7ffe6911", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: Cannot substitute by variable that gets substituted itself: y <- x 1\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper import OptimizationError\n", + "\n", + "try:\n", + " sub = mod.substitute_variables(constants={\"x\": -1}, variables={\"y\": (\"x\", 1)})\n", + "except OptimizationError as e:\n", + " print(f\"Error: {e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/addons/qiskit-addon-opt-mapper/how_tos/02-converters-for-optimization-problems.ipynb b/docs/addons/qiskit-addon-opt-mapper/how_tos/02-converters-for-optimization-problems.ipynb new file mode 100644 index 00000000000..bcb11b47ed9 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/02-converters-for-optimization-problems.ipynb @@ -0,0 +1,842 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Convert formulations of an OptimizationProblem\"\n", + "description: \"Convert formulations of an OptimizationProblem for the latest version of Optimization mapper\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "0c329473-0a09-4ae5-bb85-ef49a0faad7f", + "metadata": {}, + "source": [ + "# Convert formulations of an `OptimizationProblem`\n", + "\n", + "## Overview\n", + "Use converters in this addon to transform optimization problems into different forms. Converters change variable types, objective functions, and constraints. Quantum algorithms often require a specific formulation of an optimization problem, so you might need to convert your problem into the correct type.\n", + "\n", + "For example, the QUBO (Quadratic Unconstrained Binary Optimization) format is widely used in quantum computing. The `OptimizationProblemToQubo` converter transforms an `OptimizationProblem` into QUBO form by converting variables to binary and removing constraints. Some quantum algorithms can also handle higher-order terms. In those cases, use the `OptimizationProblemToHubo` converter to transform an `OptimizationProblem` into HUBO (Higher-order Unconstrained Binary Optimization) format.\n", + "\n", + "You can map both QUBO and HUBO problems to Ising Hamiltonians by using the `to_ising()` function. Both binary and spin variables are supported. In the Optimization Mapper Qiskit addon, these Hamiltonians are represented by the `qiskit.quantum_info.SparsePauliOp` class.\n", + "\n", + "### Converters in this module\n", + "\n", + "*Variables*\n", + "- **BinaryToSpin**: Converts binary variables to spin variables.\n", + "- **SpinToBinary**: Converts spin variables to binary variables.\n", + "- **IntegerToBinary**: Converts integer variables to binary variables.\n", + "\n", + "*Constraints*\n", + "- **InequalityToEquality**: Converts inequality constraints to equality constraints by using slack variables.\n", + "- **EqualityToPenalty**: Converts equality constraints to penalty terms in the objective function.\n", + "- **LinearInequalityToPenalty**: Converts specific linear inequality constraints to penalty terms in the objective function with fewer additional variables.\n", + "\n", + "*Objectives*\n", + "- **MaximizeToMinimize**: Converts a maximization objective to a minimization objective.\n", + "- **MinimizeToMaximize**: Converts a minimization objective to a maximization objective.\n", + "\n", + "*End-to-end*\n", + "- **OptimizationProblemToQubo**: Converts an `OptimizationProblem` to QUBO format. Combines several converters: `IntegerToBinary`, `InequalityToPenalty`, `InequalityToEquality`, `EqualityToPenalty`, and `MaximizeToMinimize`. Raises an exception if higher-order terms are present.\n", + "- **OptimizationProblemToHubo**: Converts an `OptimizationProblem` to HUBO format. Combines several converters: `IntegerToBinary`, `InequalityToPenalty`, `EqualityToPenalty`, and `MaximizeToMinimize`, while preserving higher-order terms in the objective function." + ] + }, + { + "cell_type": "markdown", + "id": "a846cc6f-284c-4f97-b6d8-c4db3f0a91da", + "metadata": {}, + "source": [ + "## Code examples\n", + "\n", + "### Integer to binary conversion\n", + "`IntegerToBinary` converts integer variables into binary variables and adjusts the corresponding coefficients to eliminate integer variables from an `OptimizationProblem`. For this conversion, the bounded-coefficient encoding proposed in [arxiv:1706.01945](https://arxiv.org/abs/1706.01945) (Eq. (5)) is used. In this encoding, an integer variable $x$ with bounds $l \\leq x \\leq u$ is represented using binary variables $b_i$ as follows:\n", + "\n", + "$x = l + \\sum_{i=0}^{k-1} 2^i b_i + (u - l + 1 - 2^k) b_k$\n", + "where $k = \\lceil \\log_2(u - l + 1) \\rceil$. This encoding minimizes the number of binary variables required to represent the integer variable while ensuring that the bounds are respected.\n", + "\n", + "For more details on the encoding method, please refer to the paper." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9a4fb20f-6a23-4cca-b56d-ae6309d69b17", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Integer to Binary Example\n", + "\n", + "Minimize\n", + " x^2*y + x^2 + 2*x\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x + y <= 2 'c1'\n", + "\n", + " Integer variables (1)\n", + " 0 <= x <= 3\n", + "\n", + " Binary variables (1)\n", + " y\n", + "\n", + "\n", + "Converted Problem:\n", + "Problem name: Integer to Binary Example\n", + "\n", + "Minimize\n", + " x@0^2*y + 4*x@0*x@1*y + 4*x@1^2*y + x@0^2 + 4*x@0*x@1 + 4*x@1^2 + 2*x@0\n", + " + 4*x@1\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x@0 + 2*x@1 + y <= 2 'c1'\n", + "\n", + " Binary variables (3)\n", + " x@0 x@1 y\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper import OptimizationProblem\n", + "from qiskit_addon_opt_mapper.converters import IntegerToBinary\n", + "\n", + "# Create an optimization problem with integer variables\n", + "op = OptimizationProblem(\"Integer to Binary Example\")\n", + "op.integer_var(name=\"x\", lowerbound=0, upperbound=3)\n", + "op.binary_var(name=\"y\")\n", + "\n", + "op.minimize(linear={\"x\": 2}, quadratic={(\"x\", \"x\"): 1}, higher_order={3: {(\"x\", \"x\", \"y\"): 1}})\n", + "op.linear_constraint(linear={\"x\": 1, \"y\": 1}, sense=\"LE\", rhs=2, name=\"c1\")\n", + "print(\"Original Problem:\")\n", + "print(op.prettyprint())\n", + "# Convert integer variables to binary variables\n", + "int_to_bin = IntegerToBinary()\n", + "op_bin = int_to_bin.convert(op)\n", + "print(\"\\nConverted Problem:\")\n", + "print(op_bin.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "80036c42-d82d-4ca3-a685-e34b41aa022f", + "metadata": {}, + "source": [ + "## Binary to spin conversion\n", + "The `BinaryToSpin` converter transforms binary variables (0/1) into spin variables (-1/+1) using the relation $b = (1 - s)/2$, where $b$ is a binary variable and $s$ is a spin variable. This conversion is particularly useful in quantum computing contexts, where spin variables are often preferred." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9e94dbd3-1cec-4d1f-b52a-139c046a8c25", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Binary Problem:\n", + "Problem name: Binary to Spin Example\n", + "\n", + "Minimize\n", + " b1*b2 + b1 + b2\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " b1 b2\n", + "\n", + "\n", + "Converted Spin Problem:\n", + "Problem name: Binary to Spin Example\n", + "\n", + "Minimize\n", + " 0.25*b1@spin*b2@spin - 0.75*b1@spin - 0.75*b2@spin + 1.25\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Spin variables (2)\n", + " -1 <= b1@spin <= 1\n", + " -1 <= b2@spin <= 1\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import BinaryToSpin\n", + "\n", + "# Convert binary variables to spin variables\n", + "op_bin = OptimizationProblem(\"Binary to Spin Example\")\n", + "op_bin.binary_var(name=\"b1\")\n", + "op_bin.binary_var(name=\"b2\")\n", + "op_bin.minimize(linear={\"b1\": 1, \"b2\": 1}, quadratic={(\"b1\", \"b2\"): 1})\n", + "print(\"Original Binary Problem:\")\n", + "print(op_bin.prettyprint())\n", + "\n", + "# Convert binary variables to spin variables\n", + "bin_to_spin = BinaryToSpin()\n", + "op_spin = bin_to_spin.convert(op_bin)\n", + "print(\"\\nConverted Spin Problem:\")\n", + "print(op_spin.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "c31722ed-5882-4b0b-9fe2-16c0c9857675", + "metadata": {}, + "source": [ + "## Spin to binary conversion\n", + "The `SpinToBinary` converter transforms spin variables (-1/+1) into binary variables (0/1) using the relation $s = 1 - 2b$, where $s$ is a spin variable and $b$ is a binary variable. This conversion is useful when an optimization problem needs to be expressed in terms of binary variables for certain algorithms or solvers." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c3dda8dd-98b0-41af-8691-9d052395466b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Spin Problem:\n", + "Problem name: Spin to Binary Example\n", + "\n", + "Minimize\n", + " s1*s2 + s1 + s2\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Spin variables (2)\n", + " -1 <= s1 <= 1\n", + " -1 <= s2 <= 1\n", + "\n", + "\n", + "Converted Binary Problem:\n", + "Problem name: Spin to Binary Example\n", + "\n", + "Minimize\n", + " 4*s1@bin*s2@bin - 4*s1@bin - 4*s2@bin + 3\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " s1@bin s2@bin\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import SpinToBinary\n", + "\n", + "op_spin2 = OptimizationProblem(\"Spin to Binary Example\")\n", + "op_spin2.spin_var(name=\"s1\")\n", + "op_spin2.spin_var(name=\"s2\")\n", + "op_spin2.minimize(linear={\"s1\": 1, \"s2\": 1}, quadratic={(\"s1\", \"s2\"): 1})\n", + "print(\"Original Spin Problem:\")\n", + "print(op_spin2.prettyprint())\n", + "# Convert spin variables to binary variables\n", + "spin_to_bin = SpinToBinary()\n", + "op_bin2 = spin_to_bin.convert(op_spin2)\n", + "print(\"\\nConverted Binary Problem:\")\n", + "print(op_bin2.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "d946ee88-2252-4bdd-969e-a9154e5c25b4", + "metadata": {}, + "source": [ + "## Inequality to equality conversion\n", + "The `InequalityToEquality` converter transforms inequality constraints into equality constraints by introducing slack variables. For example, an inequality constraint of the form:\n", + "$ax + by \\leq c$\n", + "is converted to an equality constraint:\n", + "$ax + by + s = c$\n", + "where $s$ is a slack variable that is constrained to be non-negative ($s \\geq 0$). This conversion allows the optimization problem to be expressed entirely in terms of equality constraints, which can be beneficial for certain optimization algorithms that require equality constraints." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0e0be0fd-f257-4366-9d65-3a5899e66580", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Inequality to Equality Example\n", + "\n", + "Minimize\n", + " x + y\n", + "\n", + "Subject to\n", + " Linear constraints (2)\n", + " x + y <= 5 'c1'\n", + " 10*x >= 3 'c2'\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n", + "\n", + "Converted Problem:\n", + "Problem name: Inequality to Equality Example\n", + "\n", + "Minimize\n", + " x + y\n", + "\n", + "Subject to\n", + " Linear constraints (2)\n", + " c1@int_slack + x + y == 5 'c1'\n", + " -c2@int_slack + 10*x == 3 'c2'\n", + "\n", + " Integer variables (2)\n", + " 0 <= c1@int_slack <= 5\n", + " 0 <= c2@int_slack <= 7\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import InequalityToEquality\n", + "\n", + "# Create an optimization problem with an inequality constraint\n", + "op2 = OptimizationProblem(\"Inequality to Equality Example\")\n", + "op2.binary_var(name=\"x\")\n", + "op2.binary_var(name=\"y\")\n", + "op2.minimize(linear={\"x\": 1, \"y\": 1})\n", + "op2.linear_constraint(linear={\"x\": 1, \"y\": 1}, sense=\"LE\", rhs=5, name=\"c1\")\n", + "op2.linear_constraint(linear={\"x\": 10}, sense=\"GE\", rhs=3, name=\"c2\")\n", + "print(\"Original Problem:\")\n", + "print(op2.prettyprint())\n", + "# Convert inequality constraints to equality constraints\n", + "ineq_to_eq = InequalityToEquality()\n", + "op_eq = ineq_to_eq.convert(op2)\n", + "print(\"\\nConverted Problem:\")\n", + "print(op_eq.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "92643b58-d3dd-4a1a-a315-3a33649c6219", + "metadata": {}, + "source": [ + "## Equality to penalty conversion\n", + "The `EqualityToPenalty` converter transforms equality constraints into penalty terms added to the objective function.\n", + "\\begin{array}{}\n", + "\\text { Equality constraint } & & \\text { Penalty term } \\\\\n", + "ax + by = c & \\rightarrow & P(c - (ax + by))^2 \\\\\n", + "\\end{array}\n", + "where $P$ is a large positive/negative constant that penalizes deviations from the equality constraint (when the problem is a minimizing problem, $P$ should be a large positive constant; conversely, when the problem is a maximizing problem, $P$ should be a large negative constant). This approach effectively incorporates the equality constraints into the objective function, allowing the optimization algorithm to focus on minimizing or maximizing the modified objective function while still respecting the original constraints.\n", + "If $P$ is not specified, it is automatically set to a value larger than the sum of the absolute values of the coefficients in the objective function." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8a39358d-81ea-4af6-8ccb-7b41e7e1abf6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Equality to Penalty Example\n", + "\n", + "Minimize\n", + " 0\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x + y == 3 'c1'\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n", + "\n", + "Converted Problem:\n", + "Problem name: \n", + "\n", + "Minimize\n", + " 100000*x^2 + 200000*x*y + 100000*y^2 - 600000*x - 600000*y + 900000\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import EqualityToPenalty\n", + "\n", + "# Create an optimization problem with an equality constraint\n", + "op3 = OptimizationProblem(\"Equality to Penalty Example\")\n", + "op3.binary_var(name=\"x\")\n", + "op3.binary_var(name=\"y\")\n", + "op3.linear_constraint(linear={\"x\": 1, \"y\": 1}, sense=\"EQ\", rhs=3, name=\"c1\")\n", + "print(\"Original Problem:\")\n", + "print(op3.prettyprint())\n", + "# Convert equality constraints to penalty terms in the objective function\n", + "eq_to_penalty = EqualityToPenalty(penalty=1e5)\n", + "op_penalty = eq_to_penalty.convert(op3)\n", + "print(\"\\nConverted Problem:\")\n", + "print(op_penalty.prettyprint())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b6b7bd90-5a8b-4ca7-afac-a808282bd5b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Converted Problem with Automatic Penalty:\n", + "Problem name: \n", + "\n", + "Minimize\n", + " x^2 + 2*x*y + y^2 - 6*x - 6*y + 9\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "# Automatic penalty value\n", + "eq_to_penalty_auto = EqualityToPenalty()\n", + "op_penalty_auto = eq_to_penalty_auto.convert(op3)\n", + "print(\"\\nConverted Problem with Automatic Penalty:\")\n", + "print(op_penalty_auto.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "25a76025-6101-4185-9002-645311c02100", + "metadata": {}, + "source": [ + "## Linear inequality to penalty conversion\n", + "The `LinearInequalityToPenalty` converter transforms specific binary linear inequality constraints into penalty terms added to\n", + "the objective function with a fewer number of additional variables compared to the `InequalityToEquality` converter followed by the `EqualityToPenalty` converter.\n", + "\n", + "\\begin{array}{}\n", + "\\text { Inequality constraint } & & \\text { Penalty term } \\\\\n", + "x \\leq y & \\rightarrow & P(x-x y) \\\\\n", + "x \\geq y & \\rightarrow & P(y-x y) \\\\\n", + "\\sum_{i=1}^n x_i \\leq 1, n \\geq 2 & \\rightarrow &\n", + " P \\sum_{i, j : i < j} x_i x_j\\\\\n", + "\\sum_{i=1}^n x_i \\geq n-1, n \\geq 2 & \\rightarrow &\n", + " P \\sum_{i, j : i < j} (1 - x_i) (1 - x_j)\n", + "\\end{array}\n", + "\n", + "*Note that x, y, z and `x_i` are binary variables, and P is a penalty factor,where the value of P is automatically determined or supplied by users.*" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "362fa702-59fe-4bdd-9da0-74ba5e44afb6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Linear Inequality to Penalty Example\n", + "\n", + "Minimize\n", + " 0\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x - y <= 0 'c1'\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n", + "\n", + "Converted Problem:\n", + "Problem name: Linear Inequality to Penalty Example\n", + "\n", + "Minimize\n", + " -100000*x*y + 100000*x\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "# Linear Inequality to Penalty Conversion\n", + "from qiskit_addon_opt_mapper.converters import LinearInequalityToPenalty\n", + "\n", + "# Create an optimization problem with a specific linear inequality constraint\n", + "op4 = OptimizationProblem(\"Linear Inequality to Penalty Example\")\n", + "op4.binary_var(name=\"x\")\n", + "op4.binary_var(name=\"y\")\n", + "op4.linear_constraint(linear={\"x\": 1, \"y\": -1}, sense=\"LE\", rhs=0, name=\"c1\") # x <= y\n", + "print(\"Original Problem:\")\n", + "print(op4.prettyprint())\n", + "# Convert specific linear inequality constraints to penalty terms in the objective function\n", + "lin_ineq_to_penalty = LinearInequalityToPenalty(penalty=1e5)\n", + "op4_penalty = lin_ineq_to_penalty.convert(op4)\n", + "print(\"\\nConverted Problem:\")\n", + "print(op4_penalty.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "86411dd9-27d5-4f4f-89c6-a84aa7c3ae41", + "metadata": {}, + "source": [ + "## Maximize to minimize conversion\n", + "The `MaximizeToMinimize` converter transforms a maximization objective into a minimization objective by negating the coefficients of the objective function.\n", + "\\begin{array}{}\n", + "\\text { Maximization objective } & & \\text { Minimization objective } \\\\\n", + "\\max (ax + by) & \\rightarrow & \\min - (ax + by)\n", + "\\end{array}\n", + "The `MinimizeToMaximize` converter performs the inverse operation, converting a minimization objective into a maximization objective by negating the coefficients of the objective function." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "349388e2-2a54-428d-b468-29f40f65fafc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Maximize to Minimize Example\n", + "\n", + "Maximize\n", + " x + 2*y\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n", + "\n", + "Converted Problem:\n", + "Problem name: Maximize to Minimize Example\n", + "\n", + "Minimize\n", + " -x - 2*y\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import MaximizeToMinimize, MinimizeToMaximize\n", + "\n", + "# Create an optimization problem with a maximization objective\n", + "op5 = OptimizationProblem(\"Maximize to Minimize Example\")\n", + "op5.binary_var(name=\"x\")\n", + "op5.binary_var(name=\"y\")\n", + "op5.maximize(linear={\"x\": 1, \"y\": 2})\n", + "print(\"Original Problem:\")\n", + "print(op5.prettyprint())\n", + "# Convert maximization objective to minimization objective\n", + "max_to_min = MaximizeToMinimize()\n", + "op5_min = max_to_min.convert(op5)\n", + "print(\"\\nConverted Problem:\")\n", + "print(op5_min.prettyprint())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bbc29dad-9f31-4a04-a6b6-88ac51da2865", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Re-converted Problem:\n", + "Problem name: Maximize to Minimize Example\n", + "\n", + "Maximize\n", + " x + 2*y\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (2)\n", + " x y\n", + "\n" + ] + } + ], + "source": [ + "# Minimize to Maximize Conversion\n", + "min_to_max = MinimizeToMaximize()\n", + "op5_max = min_to_max.convert(op5_min)\n", + "print(\"\\nRe-converted Problem:\")\n", + "print(op5_max.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "591e8c14-2ad2-4567-864f-a04530f51dec", + "metadata": {}, + "source": [ + "## QUBO conversion\n", + "The `OptimizationProblemToQubo` converter transforms an `OptimizationProblem` in quadratic format (quadratic objective function with linear constraints)\n", + "into QUBO form by converting variables to binary and eliminating constraints. It combines several converters: `IntegerToBinary`, `SpinToBinary`, `InequalityToPenalty`, `InequalityToEquality`, `EqualityToPenalty`, and `MaximizeToMinimize`.\n", + "The resulting QUBO problem can be directly mapped to an Ising Hamiltonian using the `to_ising()` function." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "c0cf153c-7e69-496d-b0fe-da2a47d777b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Quadratic Program to QUBO Example\n", + "\n", + "Minimize\n", + " x*y + 2*x\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x + y <= 5 'c1'\n", + "\n", + " Integer variables (1)\n", + " 0 <= x <= 3\n", + "\n", + " Binary variables (1)\n", + " y\n", + "\n", + "\n", + "Converted QUBO Problem:\n", + "Problem name: \n", + "\n", + "Minimize\n", + " 100000*c1@int_slack@0^2 + 400000*c1@int_slack@0*c1@int_slack@1\n", + " + 400000*c1@int_slack@0*c1@int_slack@2 + 400000*c1@int_slack@1^2\n", + " + 800000*c1@int_slack@1*c1@int_slack@2 + 400000*c1@int_slack@2^2\n", + " + 200000*x@0*c1@int_slack@0 + 400000*x@0*c1@int_slack@1\n", + " + 400000*x@0*c1@int_slack@2 + 100000*x@0^2 + 400000*x@0*x@1 + 200001*x@0*y\n", + " + 400000*x@1*c1@int_slack@0 + 800000*x@1*c1@int_slack@1\n", + " + 800000*x@1*c1@int_slack@2 + 400000*x@1^2 + 400002*x@1*y\n", + " + 200000*y*c1@int_slack@0 + 400000*y*c1@int_slack@1 + 400000*y*c1@int_slack@2\n", + " + 100000*y^2 - 1000000*c1@int_slack@0 - 2000000*c1@int_slack@1\n", + " - 2000000*c1@int_slack@2 - 999998*x@0 - 1999996*x@1 - 1000000*y + 2500000\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (6)\n", + " x@0 x@1 y c1@int_slack@0 c1@int_slack@1 c1@int_slack@2\n", + "\n", + "penalty: 100000.0\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo\n", + "\n", + "# Create a optimization problem with various constraints\n", + "op6 = OptimizationProblem(\"Quadratic Program to QUBO Example\")\n", + "op6.integer_var(name=\"x\", lowerbound=0, upperbound=3)\n", + "op6.binary_var(name=\"y\")\n", + "op6.minimize(linear={\"x\": 2}, quadratic={(\"x\", \"y\"): 1})\n", + "op6.linear_constraint(linear={\"x\": 1, \"y\": 1}, sense=\"LE\", rhs=5, name=\"c1\")\n", + "print(\"Original Problem:\")\n", + "print(op6.prettyprint())\n", + "# Convert the optimization problem to QUBO format\n", + "qubo_converter = OptimizationProblemToQubo(penalty=1e5)\n", + "op6_qubo = qubo_converter.convert(op6)\n", + "print(\"\\nConverted QUBO Problem:\")\n", + "print(op6_qubo.prettyprint())\n", + "print(f\"penalty: {qubo_converter.penalty}\")" + ] + }, + { + "cell_type": "markdown", + "id": "25d6a591-8270-40d5-abba-1a0ddb31b255", + "metadata": {}, + "source": [ + "## HUBO conversion\n", + "The `OptimizationProblemToHubo` converter transforms an `OptimizationProblem` into a HUBO format (higher-order objective function with no constraints) by converting variables to binary and eliminating constraints. It combines several converters: `IntegerToBinary`, `SpinToBinary`, `InequalityToPenalty`, `EqualityToPenalty`, and `MaximizeToMinimize`, while preserving higher-order terms in the objective function. The resulting HUBO problem can be directly mapped to an Ising Hamiltonian using the `to_ising()` function." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "26837fff-19da-444f-a9b1-8007e89f4556", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Problem:\n", + "Problem name: Optimization Problem to HUBO Example\n", + "\n", + "Minimize\n", + " x*y*z + x*y + 2*x\n", + "\n", + "Subject to\n", + " Higher-order constraints (1)\n", + " x*y*z == 2 'c1'\n", + "\n", + " Integer variables (1)\n", + " 0 <= x <= 3\n", + "\n", + " Binary variables (2)\n", + " y z\n", + "\n", + "\n", + "Converted HUBO Problem:\n", + "Problem name: \n", + "\n", + "Minimize\n", + " 100000*x@0^2*y^2*z^2 + 400000*x@0*x@1*y^2*z^2 + 400000*x@1^2*y^2*z^2\n", + " - 399999*x@0*y*z - 799998*x@1*y*z + x@0*y + 2*x@1*y + 2*x@0 + 4*x@1 + 400000\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (4)\n", + " x@0 x@1 y z\n", + "\n", + "penalty: 100000.0\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.converters import OptimizationProblemToHubo\n", + "\n", + "# Create an optimization problem with higher-order terms and constraints\n", + "op7 = OptimizationProblem(\"Optimization Problem to HUBO Example\")\n", + "op7.integer_var(name=\"x\", lowerbound=0, upperbound=3)\n", + "op7.binary_var(name=\"y\")\n", + "op7.binary_var(name=\"z\")\n", + "\n", + "op7.minimize(linear={\"x\": 2}, quadratic={(\"x\", \"y\"): 1}, higher_order={3: {(\"x\", \"y\", \"z\"): 1}})\n", + "op7.higher_order_constraint(higher_order={3: {(\"x\", \"y\", \"z\"): 1}}, sense=\"EQ\", rhs=2, name=\"c1\")\n", + "print(\"Original Problem:\")\n", + "print(op7.prettyprint())\n", + "# Convert the optimization problem to HUBO format\n", + "hubo_converter = OptimizationProblemToHubo(penalty=1e5)\n", + "op7_hubo = hubo_converter.convert(op7)\n", + "print(\"\\nConverted HUBO Problem:\")\n", + "print(op7_hubo.prettyprint())\n", + "print(f\"penalty: {hubo_converter.penalty}\")" + ] + }, + { + "cell_type": "markdown", + "id": "7615f2f0-c516-4618-bd9c-405c47896eeb", + "metadata": {}, + "source": [ + "## Ising transformation\n", + "Both QUBO and HUBO problems can be mapped to Ising Hamiltonians by the `to_ising()` function. In the Optimization Mapper addon, these are represented by the `qiskit.quantum_info.SparsePauliOp` object.\n", + "In transforming to the Ising Hamiltonian, the binary variables are mapped to spin variables using the relation $b_i = (1 - Z_i)/2$, where $b_i$ is a binary variable and $Z_i$ is a Pauli-Z operator acting on the $i^{th}$ qubit. The resulting Ising Hamiltonian can then be used in quantum algorithms to approximate the ground state." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "909720ca-cd91-41bf-86e8-401075011934", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Ising Hamiltonian from HUBO:\n", + "Ising Hamiltonian: SparsePauliOp(['IIIZ', 'IIZI', 'IZIZ', 'IZII', 'IZZI', 'ZIII', 'ZIIZ', 'ZZII', 'ZZIZ', 'ZIZI', 'ZZZI', 'IIII', 'IIZZ', 'IZZZ', 'ZIZZ', 'ZZZZ'],\n", + " coeffs=[ 12498.625+0.j, 24997.25 +0.j, -12499.625+0.j, 62498.875+0.j,\n", + " -24999.25 +0.j, 62499.625+0.j, -12499.875+0.j, -62499.625+0.j,\n", + " 12499.875+0.j, -24999.75 +0.j, 24999.75 +0.j, 73437.5 +0.j,\n", + " 25000. +0.j, -25000. +0.j, -25000. +0.j, 25000. +0.j])\n", + "Offset: 264066.625\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.translators import to_ising\n", + "\n", + "ising_hubo, offset = to_ising(op7_hubo)\n", + "print(\"\\nIsing Hamiltonian from HUBO:\")\n", + "print(f\"Ising Hamiltonian: {ising_hubo}\")\n", + "print(f\"Offset: {offset}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers.ipynb b/docs/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers.ipynb new file mode 100644 index 00000000000..2ecc0ec9e52 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Validate your modeling with classical solvers\"\n", + "description: \"Validate your modeling with classical solvers for the latest version of Optimization mapper\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "0a1236e8-872c-4d9b-97bc-da933fc63474", + "metadata": {}, + "source": [ + "# Validate your modeling with classical solvers\n", + "\n", + "Mapping an optimization problem to a formalism that a quantum computer can handle requires building a model. This model must be validated to ensure that its solutions correspond to the solutions of the original optimization problem we set out to solve. In this guide, we illustrate this mapping with the maximum independent set (MIS) problem. In MIS we seek the largest set of nodes of a graph $G=(V, E)$ such that no two nodes in the maximum independent set share an edge. Mathematically,\n", + "\n", + "\\begin{align}\n", + "&\\max_{x\\in\\{0,1\\}^n}\\sum_{i\\in V} x_i \\\\\n", + "&\\text{s.t.}\\quad x_i+x_j\\leq 1\\quad\\forall~(i,j)\\in E.\n", + "\\end{align}\n", + "Here, $n=|V|$ is the number of nodes of $G$, and if the decision variable $x_i=1$ then node $i$ is in the independent set. First, we will define a MIS problem instance on a small five-node graph. Such small instances are easy to solve and they allow us to test our modelling pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "03e48006-21f4-415d-872f-fa14fe046e82", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "EdgeIndices[0, 1, 2, 3, 4, 5]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import rustworkx as rx\n", + "\n", + "graph = rx.PyGraph()\n", + "\n", + "# Add 5 nodes (0 through 4)\n", + "graph.add_nodes_from([None] * 5)\n", + "\n", + "# Add edges\n", + "edges = [(0, 1, 1), (0, 4, 1), (1, 2, 1), (1, 4, 1), (2, 3, 1), (3, 4, 1)]\n", + "graph.add_edges_from(edges)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5cd95b79-e4de-48fe-8b30-da8afbdb9c77", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_opt_mapper.applications import IndependentSet\n", + "\n", + "mis = IndependentSet(graph)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "27b1b8d3-e2b7-4304-b8d8-d5e63af5deec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mis.draw()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5e6d6203-86dd-42f4-8210-c04d72b63c5e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: Independent set\n", + "\n", + "Maximize\n", + " x_0 + x_1 + x_2 + x_3 + x_4\n", + "\n", + "Subject to\n", + " Linear constraints (6)\n", + " x_0 + x_1 <= 1 'c0'\n", + " x_0 + x_4 <= 1 'c1'\n", + " x_1 + x_2 <= 1 'c2'\n", + " x_1 + x_4 <= 1 'c3'\n", + " x_2 + x_3 <= 1 'c4'\n", + " x_3 + x_4 <= 1 'c5'\n", + "\n", + " Binary variables (5)\n", + " x_0 x_1 x_2 x_3 x_4\n", + "\n" + ] + } + ], + "source": [ + "opt_problem = mis.to_optimization_problem()\n", + "print(opt_problem.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "4b3517c7-5e6c-4319-8bb2-a9a9b2b9d537", + "metadata": {}, + "source": [ + "To implement this problem on a quantum computer, we need to convert the problem to a quadratic unconstrained binary optimization problem. This requires mapping inequality constraints into equality constraints, and then adding them as penalty terms to the objective. We do this with the `LinearInequalityToPenalty` converter." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "125627ad-5ceb-4e38-8a7f-1dc244d58969", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_opt_mapper.converters import LinearInequalityToPenalty\n", + "\n", + "penalty = 0.5\n", + "converter = LinearInequalityToPenalty(penalty=penalty)\n", + "converted_prob = converter.convert(opt_problem)" + ] + }, + { + "cell_type": "markdown", + "id": "c6b153b0-c016-4899-8f6b-a31a08ba5992", + "metadata": {}, + "source": [ + "The result of the conversion is the optimization problem shown below. This problem no longer has any constraints. The objective is then easily converted into an Ising Hamiltonian, a step that we will not show here. Instead, we ask the question: *\"Is the unconstrained model of the MIS problem a good one?\"* Indeed, we made the choice to set `penalty = 0.5`, but was this a good choice?" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "6407eb00-7837-45e7-8364-fac1fcaa993b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: Independent set\n", + "\n", + "Maximize\n", + " -0.5*x_0*x_1 - 0.5*x_0*x_4 - 0.5*x_1*x_2 - 0.5*x_1*x_4 - 0.5*x_2*x_3\n", + " - 0.5*x_3*x_4 + x_0 + x_1 + x_2 + x_3 + x_4\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (5)\n", + " x_0 x_1 x_2 x_3 x_4\n", + "\n" + ] + } + ], + "source": [ + "print(converted_prob.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "092b21aa-0d78-43a9-95ed-cdaef9ed42d2", + "metadata": {}, + "source": [ + "To answer the question above, we need to solve our model and see if the solution is indeed an independent set. We do this with a classical solver. Here, we use CPLEX, which at this small scale runs very efficiently. The code below imports the solver, solves the model, and interprets the result as a candidate solution to our original MIS optimization problem." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9619dc26-87f2-40be-9636-db7481463d46", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_opt_mapper.solvers import CplexSolver\n", + "\n", + "validation_solver = CplexSolver()\n", + "\n", + "result = validation_solver.solve(converted_prob)\n", + "y = converter.interpret(result.x)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f7035c7d-650b-4474-be9c-5eba30235051", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Objective value 4.0\n", + "Is y feasible? False\n" + ] + } + ], + "source": [ + "print(\"Objective value\", opt_problem.objective.evaluate(y))\n", + "print(\"Is y feasible?\", opt_problem.is_feasible(y))" + ] + }, + { + "cell_type": "markdown", + "id": "c61b4f08-dbd2-492c-81ad-7c7427fa06f2", + "metadata": {}, + "source": [ + "The result above indicates that the solution to our model is an infeasible solution to the MIS problem. This is problematic and indicates that something went wrong in the problem modeling. Indeed, the conversion of the constraints into penalty terms is not strong enough to enforce the independent set conditions. To remedy this situation, we change the modeling and increase the value of the penalty." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fb9fe906-618a-4c51-9076-d77c838b4b9f", + "metadata": {}, + "outputs": [], + "source": [ + "penalty2 = 2\n", + "converter2 = LinearInequalityToPenalty(penalty=penalty2)\n", + "converted_prob2 = converter2.convert(opt_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2fffd4af-347c-4f20-b05a-fe46dfc40140", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: Independent set\n", + "\n", + "Maximize\n", + " -2*x_0*x_1 - 2*x_0*x_4 - 2*x_1*x_2 - 2*x_1*x_4 - 2*x_2*x_3 - 2*x_3*x_4 + x_0\n", + " + x_1 + x_2 + x_3 + x_4\n", + "\n", + "Subject to\n", + " No constraints\n", + "\n", + " Binary variables (5)\n", + " x_0 x_1 x_2 x_3 x_4\n", + "\n" + ] + } + ], + "source": [ + "print(converted_prob2.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "19aa8f2a-2f6e-4395-b8b8-31bb0a7348d5", + "metadata": {}, + "source": [ + "Now, we solve this new model with CPLEX." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0708bf53-98ec-4359-b083-269b069b8292", + "metadata": {}, + "outputs": [], + "source": [ + "result2 = validation_solver.solve(converted_prob2)\n", + "y2 = converter.interpret(result2.x)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "49292e2f-5727-4d1a-a566-3c0178b14e4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Objective value 2.0\n", + "Is y feasible? True\n" + ] + } + ], + "source": [ + "print(\"Objective value\", opt_problem.objective.evaluate(y2))\n", + "print(\"Is y feasible?\", opt_problem.is_feasible(y2))" + ] + }, + { + "cell_type": "markdown", + "id": "3c4235ab-7ed4-4993-961d-624ce2fe3782", + "metadata": {}, + "source": [ + "We observe that the optimal solution found by CPLEX is feasible. We can also visualize the solution form the bad model and the one from the good model. As seen below, there are multiple nodes in the bad solution `y` that share an edge and are therefore not an independent set. Here we see how modeling assumptions play an important role in the type of solution obtained, and the importance of testing the modeling pipeline with small problem instances that can be efficiently solved." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f1281552-b611-4911-bdb9-df7d1df97ad4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mis.draw(y)" + ] + }, + { + "cell_type": "markdown", + "id": "dc0bf66c-1653-4998-a38f-7dc8b4bf50bc", + "metadata": {}, + "source": [ + "In the good solution `y2`, drawn below, we see that no two nodes share an edge and are therefore an independent set." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "309f88e4-5d1f-47f8-b63a-50346829ffeb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mis.draw(y2)" + ] + } + ], + "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-opt-mapper/how_tos/04-translate-docplex.ipynb b/docs/addons/qiskit-addon-opt-mapper/how_tos/04-translate-docplex.ipynb new file mode 100644 index 00000000000..df97470b4c7 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/04-translate-docplex.ipynb @@ -0,0 +1,260 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Translate between OptimizationProblem and Docplex\"\n", + "description: \"Translate between OptimizationProblem and Docplex for the latest version of Optimization mapper\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "54445be0-0780-4877-9633-503da91cf288", + "metadata": {}, + "source": [ + "# Translate between `OptimizationProblem` and Docplex\n", + "\n", + "If you are familiar with the Docplex workflow, you can seamlessly translate between a Docplex model and an `OptimizationProblem` with the `from_docplex_mp` and `to_docplex_mp` translators. With this translation, you can take advantage of Docplex capabilities, such as **LP file loading**, without many additional steps.\n", + "\n", + "\n", + "
\n", + "Note: Docplex does not support higher-order terms in its formulation. This conversion is limited to constant, linear, and quadratic terms.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4fd054a2-382e-405d-80a7-b6a9fa601a96", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\ This file has been generated by DOcplex\n", + "\\ ENCODING=ISO-8859-1\n", + "\\Problem name: docplex model\n", + "\n", + "Minimize\n", + " obj: x + [ 4 x*y - 2 z^2 ]/2 + 3\n", + "Subject To\n", + " lin_eq: x + 2 y = 3\n", + " lin_leq: x + 2 y <= 3\n", + " lin_geq: x + 2 y >= 3\n", + "\n", + "Bounds\n", + " 0 <= x <= 1\n", + " -1 <= y <= 5\n", + " -1 <= z <= 5\n", + "\n", + "Binaries\n", + " x\n", + "\n", + "Generals\n", + " y\n", + "End\n", + "\n" + ] + } + ], + "source": [ + "from docplex.mp.model import Model\n", + "\n", + "docplex_mod = Model(name=\"docplex model\")\n", + "x = docplex_mod.binary_var(name=\"x\")\n", + "y = docplex_mod.integer_var(name=\"y\", lb=-1, ub=5)\n", + "z = docplex_mod.continuous_var(name=\"z\", lb=-1, ub=5)\n", + "\n", + "docplex_mod.minimize(3 + x + 2 * x * y - z * z)\n", + "docplex_mod.add_constraint(x + 2 * y == 3, \"lin_eq\")\n", + "docplex_mod.add_constraint(x + 2 * y <= 3, \"lin_leq\")\n", + "docplex_mod.add_constraint(x + 2 * y >= 3, \"lin_geq\")\n", + "\n", + "print(docplex_mod.export_as_lp_string())" + ] + }, + { + "cell_type": "markdown", + "id": "7a7f0cfa-22ab-482a-8954-88f02b374b25", + "metadata": {}, + "source": [ + "When you display your problem in LP format using `export_as_lp_string`,\n", + "`Binaries` denotes binary variables and `Generals` denotes integer variables.\n", + "\n", + "If variables are not included in either `Binaries` or `Generals`, such variables are continuous ones with default lower bound == 0 and upper bound == infinity.\n", + "\n", + "Note that you cannot use `'e`' or `'E'` as the first character of names due to the [specification of LP format](https://www.ibm.com/docs/en/icos/22.1.2?topic=representation-variable-names-in-lp-file-format).\n", + "\n", + "The following snippet shows the `from_docplex_mp` translator:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2580db2a-3b28-40c6-8103-a6a743eb712e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: docplex model\n", + "\n", + "Minimize\n", + " 2*x*y - z^2 + x + 3\n", + "\n", + "Subject to\n", + " Linear constraints (3)\n", + " x + 2*y == 3 'lin_eq'\n", + " x + 2*y <= 3 'lin_leq'\n", + " x + 2*y >= 3 'lin_geq'\n", + "\n", + " Integer variables (1)\n", + " -1 <= y <= 5\n", + "\n", + " Continuous variables (1)\n", + " -1 <= z <= 5\n", + "\n", + " Binary variables (1)\n", + " x\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.translators import from_docplex_mp\n", + "\n", + "mod2 = from_docplex_mp(docplex_mod)\n", + "print(mod2.prettyprint())" + ] + }, + { + "cell_type": "markdown", + "id": "fd3416e9-47cc-4956-a891-11ea307837a6", + "metadata": {}, + "source": [ + "As stated before, you can also convert a `OptimizationProblem` to a Docplex model using the `to_docplex` function." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c1cc6e5b-ec69-4fac-804b-5bef4aed44b6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\ This file has been generated by DOcplex\n", + "\\ ENCODING=ISO-8859-1\n", + "\\Problem name: docplex model\n", + "\n", + "Minimize\n", + " obj: x + [ 4 x*y - 2 z^2 ]/2 + 3\n", + "Subject To\n", + " lin_eq: x + 2 y = 3\n", + " lin_leq: x + 2 y <= 3\n", + " lin_geq: x + 2 y >= 3\n", + "\n", + "Bounds\n", + " 0 <= x <= 1\n", + " -1 <= y <= 5\n", + " -1 <= z <= 5\n", + "\n", + "Binaries\n", + " x\n", + "\n", + "Generals\n", + " y\n", + "End\n", + "\n" + ] + } + ], + "source": [ + "from qiskit_addon_opt_mapper.translators import to_docplex_mp\n", + "\n", + "docplex_mod2 = to_docplex_mp(mod2)\n", + "print(docplex_mod2.export_as_lp_string())" + ] + }, + { + "cell_type": "markdown", + "id": "63007e1d-cfac-4870-896c-4c7214921d50", + "metadata": {}, + "source": [ + "## Load LP files using Docplex" + ] + }, + { + "cell_type": "markdown", + "id": "c9a20a5b-807c-4f9c-9e25-ab9fc05d1cae", + "metadata": {}, + "source": [ + "Once you are familia with the Docplex translators, loading from LP files becomes very simple. You can use the `docplex.mp.ModelReader` class followed by a translation step:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "875f9ab1-f624-4a15-bec4-ee303ebb4ea9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Problem name: my_lp_file\n", + "\n", + "Minimize\n", + " 2*x0^2 + 0.5*x0*x1 + 0.5*x1^2 + 2*x0 + 2*x1\n", + "\n", + "Subject to\n", + " Linear constraints (1)\n", + " x0 + x1 == 2 'constraint'\n", + "\n", + " Integer variables (2)\n", + " 0 <= x0 <= 4\n", + " 0 <= x1 <= 4\n", + "\n" + ] + } + ], + "source": [ + "from docplex.mp.model_reader import ModelReader\n", + "\n", + "model = ModelReader.read(\"data/my_lp_file.lp\")\n", + "\n", + "op3 = from_docplex_mp(model)\n", + "print(op3.prettyprint())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/addons/qiskit-addon-opt-mapper/how_tos/index.mdx b/docs/addons/qiskit-addon-opt-mapper/how_tos/index.mdx new file mode 100644 index 00000000000..fc31188fdbe --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/how_tos/index.mdx @@ -0,0 +1,41 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Optimization mapper" +--- + +# How-To Guides + +How-To Guides + +* [Migrate from `qiskit-optimization` to `qiskit-addon-opt-mapper`](00-migrate-from-qiskit-optimization) + + * [Summary](00-migrate-from-qiskit-optimization#summary) + * [Tables](00-migrate-from-qiskit-optimization#tables) + +* [Model quantum optimization problems with the `OptimizationProblem` class](01-optimization-problem) + + * [Introduction](01-optimization-problem#introduction) + * [Construct an `OptimizationProblem`](01-optimization-problem#construct-an-optimizationproblem) + * [Add or remove linear, quadratic, and higher-order terms to and from the constraints](01-optimization-problem#add-or-remove-linear,-quadratic,-and-higher-order-terms-to-and-from-the-constraints) + * [Substitute variables](01-optimization-problem#substitute-variables) + +* [Convert formulations of an `OptimizationProblem`](02-converters-for-optimization-problems) + + * [Overview](02-converters-for-optimization-problems#overview) + * [Code examples](02-converters-for-optimization-problems#code-examples) + * [Binary to spin conversion](02-converters-for-optimization-problems#binary-to-spin-conversion) + * [Spin to binary conversion](02-converters-for-optimization-problems#spin-to-binary-conversion) + * [Inequality to equality conversion](02-converters-for-optimization-problems#inequality-to-equality-conversion) + * [Equality to penalty conversion](02-converters-for-optimization-problems#equality-to-penalty-conversion) + * [Linear inequality to penalty conversion](02-converters-for-optimization-problems#linear-inequality-to-penalty-conversion) + * [Maximize to minimize conversion](02-converters-for-optimization-problems#maximize-to-minimize-conversion) + * [QUBO conversion](02-converters-for-optimization-problems#qubo-conversion) + * [HUBO conversion](02-converters-for-optimization-problems#hubo-conversion) + * [Ising transformation](02-converters-for-optimization-problems#ising-transformation) + +* [Validate your modeling with classical solvers](03-validate-with-solvers) + +* [Translate between `OptimizationProblem` and Docplex](04-translate-docplex) + + * [Load LP files using Docplex](04-translate-docplex#load-lp-files-using-docplex) + diff --git a/docs/addons/qiskit-addon-opt-mapper/index.mdx b/docs/addons/qiskit-addon-opt-mapper/index.mdx new file mode 100644 index 00000000000..adc473ffeb7 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/index.mdx @@ -0,0 +1,37 @@ +--- +title: "Optimization Mapper Qiskit addon" +description: "Documentation for the latest version of Optimization mapper" +--- + +# Optimization Mapper Qiskit addon + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package contains functionality to model optimization problems and map them to operators. For example, this package contains functions for creating models of binary optimization problems to solve with Qiskit. + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-opt-mapper' +``` + +For more installation information, refer to the [installation instructions](install) in the documentation. + +## Deprecation policy + +We follow [semantic versioning](https://semver.org/) and are guided by the principles in [Qiskit’s deprecation policy](https://github.com/Qiskit/qiskit/blob/main/DEPRECATION.md). We might 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-opt-mapper/release-notes). + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-opt-mapper). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-opt-mapper/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-opt-mapper/issues/new/choose) for tracking requests and bugs. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-opt-mapper/blob/main/LICENSE.txt) + diff --git a/docs/addons/qiskit-addon-opt-mapper/install.mdx b/docs/addons/qiskit-addon-opt-mapper/install.mdx new file mode 100644 index 00000000000..88b0642e0e7 --- /dev/null +++ b/docs/addons/qiskit-addon-opt-mapper/install.mdx @@ -0,0 +1,77 @@ +--- +title: "Installation instructions" +description: "Installation instructions for the latest version of Optimization mapper" +--- + +# Installation instructions + +There are two primary ways to run and install the packages: + +* [Option 1: Install from PyPI](#option-1) +* [Option 2: Install from source](#option-2) + +## Pre-installation + +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-opt-mapper` package is via `PyPI`. + +```sh +pip install 'qiskit-addon-opt-mapper' +``` + + + +## Option 2: Install from source + +To develop in the repository or run the notebooks locally, install from source. + +The first step is to clone the `qiskit-addon-opt-mapper` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-opt-mapper.git +``` + +Next, upgrade `pip` and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-opt-mapper +``` + +The next step is to install `qiskit-addon-opt-mapper` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you should also 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-pna/_toc.json b/docs/addons/qiskit-addon-pna/_toc.json new file mode 100644 index 00000000000..9c1b857a59a --- /dev/null +++ b/docs/addons/qiskit-addon-pna/_toc.json @@ -0,0 +1,45 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Propagated noise absorption (PNA) 0.2", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-pna" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-pna/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "How-to guides coming soon", + "url": "/docs/addons/qiskit-addon-pna/how_tos/placeholder" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/qiskit-addon-pna" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Propagated noise absorption (PNA) API reference", + "url": "/docs/api/qiskit-addon-pna" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-pna/how_tos/index.mdx b/docs/addons/qiskit-addon-pna/how_tos/index.mdx new file mode 100644 index 00000000000..f71ebabd22b --- /dev/null +++ b/docs/addons/qiskit-addon-pna/how_tos/index.mdx @@ -0,0 +1,13 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Propagated noise absorption (PNA)" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +[![](/docs/images/addons/qiskit-addon-pna/nbsphinx-no-thumbnail.svg)](placeholder) + +[How-to guides coming soon](placeholder) + diff --git a/docs/addons/qiskit-addon-pna/how_tos/placeholder.ipynb b/docs/addons/qiskit-addon-pna/how_tos/placeholder.ipynb new file mode 100644 index 00000000000..bdfeddf3f28 --- /dev/null +++ b/docs/addons/qiskit-addon-pna/how_tos/placeholder.ipynb @@ -0,0 +1,44 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"How-to guides coming soon\"\n", + "description: \"How-to guides coming soon for the latest version of Propagated noise absorption (PNA)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "3b3c0748-30e3-4bd6-b15d-b8fe80fc19ff", + "metadata": {}, + "source": [ + "# How-to guides 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-pna/index.mdx b/docs/addons/qiskit-addon-pna/index.mdx new file mode 100644 index 00000000000..37c21d89d6a --- /dev/null +++ b/docs/addons/qiskit-addon-pna/index.mdx @@ -0,0 +1,76 @@ +--- +title: "Qiskit add-on: Propagated noise absorption (PNA)" +description: "Documentation for the latest version of Propagated noise absorption (PNA)" +--- + +# Qiskit add-on: Propagated noise absorption (PNA) + +PNA is a technique for mitigating errors in observable expectation values by “absorbing” the inverses of the learned noise channels into the observable using [Pauli propagation](https://qiskit.github.io/pauli-prop/). Each Pauli noise generator in the noise model is classically propagated to the end of the circuit and applied to the observable, resulting in a new observable that when measured on a QPU, mitigates the learned gate noise. Check out the [tutorial](https://github.com/qiskit-community/qdc-challenges-2025/blob/main/day3_tutorials/Track_A/pna/propagated_noise_absorption.ipynb) to see how it works! + +## Overview + +Executing entangling gates on modern QPUs results in a substantial amount of noise. Until fully fault tolerant devices are available, ideal entangling gates, $U$, will not be available. They will instead be affected by some noise channel, $\Lambda$. + +![\_images/noisy\_expt.png](/docs/images/addons/qiskit-addon-pna/noisy_expt.avif) + +It is possible to learn and efficiently characterize this gate noise as a Pauli-Lindblad model, and as shown in probabilistic error cancellation (PEC), we can mitigate the error by implementing the anti-noise, $\Lambda^{-1}$, with a QPU sampling protocol \[1]. Other techniques, such as tensor-network error mitigation (TEM), implement the inverse noise channel as a classical post-processing step \[2]. + +![\_images/noise\_mitigated\_expt.png](/docs/images/addons/qiskit-addon-pna/noise_mitigated_expt.avif) + +Like TEM, PNA implements the inverse noise channel in a classical processing step. While TEM uses tensor networks to describe and apply the noise-mitigating map to a set of informationally complete measurements, PNA uses Pauli propagation to propagate the observable, $O$, through the inverse noise channel. This results in a new observable, $\tilde{O}$, that when measured against the noisy state, mitigates the learned noise. + +![\_images/pna\_overview.png](/docs/images/addons/qiskit-addon-pna/pna_overview.avif) + +### Sources of bias + +* This implementation propagates each Pauli error generator within each anti-noise channel, $\Lambda^{-1}_i$, to the end of the circuit. As each anti-noise generator is propagated forward through the circuit under the action of $N$ Pauli rotation gates of an $M$-qubit circuit, the number of terms will grow as $O(2^N)$ towards a maximum of $4^M$ unique Pauli components. To control the computational cost, terms with small coefficients must be truncated, which results in some error in the evolved anti-noise channel. +* In addition to the truncation of the evolved anti-noise channel, $\Lambda^{-1}$, $\tilde{O}$ is also truncated as it is propagated through $\Lambda^{-1}$. This is also a source of bias in the final mitigated expectation value. +* While letting $\tilde{O}$ grow larger during propagation will increase its accuracy, measuring it requires taking many more shots on the QPU. Typically this increases the coefficients of the original Pauli terms in $O$, along with creating many new Pauli terms with smaller coefficients. Both the rescaling of the original coefficients and the creation of new terms can increase sampling overhead. In practice, we truncate once more by measuring only the largest terms in $\tilde{O}$. + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-pna' +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Citing this project + +If you use this package in your research, please cite it according to `CITATON.bib` file included in this repository: + +```bibtex +@software{qiskit-addon-pna, + author = {Qiskit addons team}, + title = {{Qiskit addon: Propagated noise absorption}}, + howpublished = {\url{https://github.com/Qiskit/qiskit-addon-pna}}, + year = {2025} +} +``` + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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. + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-pna). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-pna/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-pna/issues/new/choose) for tracking requests and bugs. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-pna/blob/main/LICENSE.txt) + + + +## References + +\[1] Ewout van den Berg, et al., [Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors](https://arxiv.org/abs/2201.09866), arXiv:2201.09866 \[quant-ph]. + +\[2] Sergei Filippov, et al., [Scalable tensor-network error mitigation for near-term quantum computing](https://arxiv.org/abs/2307.11740), arXiv:2307.11740 \[quant-ph]. + diff --git a/docs/addons/qiskit-addon-pna/install.mdx b/docs/addons/qiskit-addon-pna/install.mdx new file mode 100644 index 00000000000..8a561fcdbbc --- /dev/null +++ b/docs/addons/qiskit-addon-pna/install.mdx @@ -0,0 +1,70 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Propagated noise absorption (PNA)" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is 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) + +## Pre-Installation + +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 +``` + + + +## Option 1: Install from PyPI + +The most straightforward way to install the `qiskit-addon-pna` package is via `PyPI`. + +```sh +pip install 'qiskit-addon-pna' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-addon-pna` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-pna.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-pna +``` + +The next step is to install `qiskit-addon-pna` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-slc/_toc.json b/docs/addons/qiskit-addon-slc/_toc.json new file mode 100644 index 00000000000..3c070c99956 --- /dev/null +++ b/docs/addons/qiskit-addon-slc/_toc.json @@ -0,0 +1,44 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Shaded lightcones 0.1", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-slc" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-slc/install" + }, + { + "title": "How-To Guides", + "url": "/docs/addons/qiskit-addon-slc/how_tos/index" + }, + { + "title": "Explanations", + "url": "/docs/addons/qiskit-addon-slc/explanations/index" + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-slc" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Shaded lightcones API reference", + "url": "/docs/api/qiskit-addon-slc" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-slc/explanations/index.mdx b/docs/addons/qiskit-addon-slc/explanations/index.mdx new file mode 100644 index 00000000000..a2e3b40f203 --- /dev/null +++ b/docs/addons/qiskit-addon-slc/explanations/index.mdx @@ -0,0 +1,9 @@ +--- +title: "Explanations" +description: "Explanations for the latest version of Shaded lightcones" +--- + +# Explanations + +There currently exists no additional content for this page. + diff --git a/docs/addons/qiskit-addon-slc/how_tos/index.mdx b/docs/addons/qiskit-addon-slc/how_tos/index.mdx new file mode 100644 index 00000000000..a4c0966d7be --- /dev/null +++ b/docs/addons/qiskit-addon-slc/how_tos/index.mdx @@ -0,0 +1,9 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Shaded lightcones" +--- + +# How-To Guides + +There currently exists no additional content for this page. + diff --git a/docs/addons/qiskit-addon-slc/index.mdx b/docs/addons/qiskit-addon-slc/index.mdx new file mode 100644 index 00000000000..6a2593ee744 --- /dev/null +++ b/docs/addons/qiskit-addon-slc/index.mdx @@ -0,0 +1,45 @@ +--- +title: "Qiskit addon: shaded lightcones (SLC)" +description: "Documentation for the latest version of Shaded lightcones" +--- + +# Qiskit addon: shaded lightcones (SLC) + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package contains the Qiskit addon for shaded lightcones (SLC). These can be used to reduce the sampling overhead of PEC. + +## Documentation + +All documentation is available [here](https://qiskit.github.io/qiskit-addon-slc/). + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install qiskit-addon-slc +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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-slc/release-notes). + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-slc). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-slc/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-slc/issues/new/choose) for tracking requests and bugs. + +## References + +1. 1. Eddins, et al. [Lightcone shading for classically accelerated quantum error mitigation](https://arxiv.org/abs/2409.04401v1), arXiv:2409.04401v1 \[quant-ph]. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-slc/blob/main/LICENSE.txt) + diff --git a/docs/addons/qiskit-addon-slc/install.mdx b/docs/addons/qiskit-addon-slc/install.mdx new file mode 100644 index 00000000000..73ffb6c2e94 --- /dev/null +++ b/docs/addons/qiskit-addon-slc/install.mdx @@ -0,0 +1,70 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Shaded lightcones" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is 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) + +## Pre-Installation + +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 +``` + + + +## Option 1: Install from PyPI + +The most straightforward way to install the `qiskit-addon-slc` package is via `PyPI`. + +```sh +pip install 'qiskit-addon-slc' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-addon-slc` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-slc.git +``` + +Next, upgrade `pip` and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-slc +``` + +The next step is to install `qiskit-addon-slc` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-sqd/_toc.json b/docs/addons/qiskit-addon-sqd/_toc.json new file mode 100644 index 00000000000..2c3e56a95db --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/_toc.json @@ -0,0 +1,83 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Sample-based quantum diagonalization (SQD) 0.12", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-sqd" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-sqd/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "Add fermionic transitions to the pool of configurations", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/add-fermionic-excitations-to-configuration-pool" + }, + { + "title": "Benchmark Pauli operator projection", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection" + }, + { + "title": "Bounding the subspace dimension", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension" + }, + { + "title": "Scale SQD chemistry workflows with Dice solver", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/integrate-dice-solver" + }, + { + "title": "Project Pauli operators onto Hilbert subspaces", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/project-pauli-operators-onto-hilbert-subspaces" + }, + { + "title": "Understand open-shell vs closed-shell options and its effect in the subspace construction", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/select-open-closed-shell" + }, + { + "title": "Optimize Hamiltonian basis with orbital optimization", + "url": "/docs/addons/qiskit-addon-sqd/how_tos/use-oo-to-optimize-hamiltonian-basis" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/qiskit-addon-sqd" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "collapsible": false, + "children": [ + { + "title": "Sample-based quantum diagonalization of a chemistry Hamiltonian", + "url": "/docs/tutorials/sample-based-quantum-diagonalization" + }, + { + "title": "Sample-based Krylov quantum diagonalization of a fermionic lattice model", + "url": "/docs/tutorials/sample-based-krylov-quantum-diagonalization" + } + ] + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Sample-based quantum diagonalization (SQD) API reference", + "url": "/docs/api/qiskit-addon-sqd" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-sqd/how_tos/add-fermionic-excitations-to-configuration-pool.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/add-fermionic-excitations-to-configuration-pool.ipynb new file mode 100644 index 00000000000..aea28db72f8 --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/add-fermionic-excitations-to-configuration-pool.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Add fermionic transitions to the pool of configurations\"\n", + "description: \"Add fermionic transitions to the pool of configurations for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "9e40af77-7f0f-4dd6-ab0a-420cf396050e", + "metadata": {}, + "source": [ + "# Add fermionic transitions to the pool of configurations\n", + "\n", + "Here we demonstrate the functionalities to augment the pool of electronic\n", + "configurations obtained by the action of transition operators on each electronic\n", + "configuration.\n", + "\n", + "We demonstrate how to add single-electron hops of the type:\n", + "$$\n", + "c^\\dagger_{p\\sigma} c_{q\\sigma} |\\textbf{x} \\rangle\n", + "$$\n", + "for $p, q = 1, ..., N_\\textrm{orb}$ and $\\sigma \\in \\{ \\uparrow, \\downarrow\\}$,\n", + "and for all $|\\textbf{x} \\rangle$ in the batch of electronic configurations." + ] + }, + { + "cell_type": "markdown", + "id": "0a7e9fcd", + "metadata": {}, + "source": [ + "Let's begin by generating a batch of random electronic configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e9506e0b-ed64-48bb-a97a-ef851b604af1", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "n_qubits = 8\n", + "n_orb = n_qubits // 2\n", + "\n", + "rand_seed = 22\n", + "np.random.seed(rand_seed)\n", + "\n", + "\n", + "# Generate some random bitstrings for testing\n", + "def random_bitstrings(n_samples, n_qubits):\n", + " return np.round(np.random.rand(n_samples, n_qubits)).astype(\"int\").astype(\"bool\")\n", + "\n", + "\n", + "bitstring_matrix = random_bitstrings(100, n_qubits)" + ] + }, + { + "cell_type": "markdown", + "id": "4155599f", + "metadata": {}, + "source": [ + "The excitation operators are specified inside a numpy array whose length is\n", + "equal to the number of fermionic nodes (or qubits). Each element of the array\n", + "must be a string that can take values:\n", + "\n", + "- ``'I'``: Identity\n", + "- ``'+'``: Creation operator\n", + "- ``'-'``: Annihilation operator\n", + "\n", + "Let's generate all possible single-electron transitions (amongst same spin\n", + "species)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "389284f3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[['I' 'I' 'I' 'I' 'I' 'I' 'I' 'I']\n", + " ['+' '-' 'I' 'I' 'I' 'I' 'I' 'I']\n", + " ['-' '+' 'I' 'I' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' '+' '-' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' '-' '+' 'I' 'I']\n", + " ['+' 'I' '-' 'I' 'I' 'I' 'I' 'I']\n", + " ['-' 'I' '+' 'I' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' '+' 'I' '-' 'I']\n", + " ['I' 'I' 'I' 'I' '-' 'I' '+' 'I']\n", + " ['+' 'I' 'I' '-' 'I' 'I' 'I' 'I']\n", + " ['-' 'I' 'I' '+' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' '+' 'I' 'I' '-']\n", + " ['I' 'I' 'I' 'I' '-' 'I' 'I' '+']\n", + " ['I' '+' '-' 'I' 'I' 'I' 'I' 'I']\n", + " ['I' '-' '+' 'I' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' 'I' '+' '-' 'I']\n", + " ['I' 'I' 'I' 'I' 'I' '-' '+' 'I']\n", + " ['I' '+' 'I' '-' 'I' 'I' 'I' 'I']\n", + " ['I' '-' 'I' '+' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' 'I' '+' 'I' '-']\n", + " ['I' 'I' 'I' 'I' 'I' '-' 'I' '+']\n", + " ['I' 'I' '+' '-' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' '-' '+' 'I' 'I' 'I' 'I']\n", + " ['I' 'I' 'I' 'I' 'I' 'I' '+' '-']\n", + " ['I' 'I' 'I' 'I' 'I' 'I' '-' '+']]\n" + ] + } + ], + "source": [ + "transitions_single = np.array(\n", + " [[\"I\" for i in range(2 * n_orb)] for j in range(4 * (n_orb**2 - n_orb) // 2 + 1)]\n", + ")\n", + "count = 1\n", + "for i in range(n_orb):\n", + " for j in range(i + 1, n_orb):\n", + " # spin up\n", + " transitions_single[count, i] = \"+\"\n", + " transitions_single[count, j] = \"-\"\n", + " count += 1\n", + " transitions_single[count, i] = \"-\"\n", + " transitions_single[count, j] = \"+\"\n", + " count += 1\n", + "\n", + " # spin down\n", + " transitions_single[count, i + n_orb] = \"+\"\n", + " transitions_single[count, j + n_orb] = \"-\"\n", + " count += 1\n", + " transitions_single[count, i + n_orb] = \"-\"\n", + " transitions_single[count, j + n_orb] = \"+\"\n", + " count += 1\n", + "\n", + "print(transitions_single)" + ] + }, + { + "cell_type": "markdown", + "id": "c15c5b3f", + "metadata": {}, + "source": [ + "Let's now apply the transition operators to the configurations in ``bitstring_matrix``" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6a44f358", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(723, 8)\n", + "[[False False False ... False False True]\n", + " [False True False ... True False False]\n", + " [ True True True ... True False True]\n", + " ...\n", + " [ True False True ... False False True]\n", + " [ True True True ... True False True]\n", + " [False False False ... False False True]]\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.fermion import enlarge_batch_from_transitions\n", + "\n", + "bitstring_matrix_aug = enlarge_batch_from_transitions(bitstring_matrix, transitions_single)\n", + "\n", + "print(bitstring_matrix_aug.shape)\n", + "print(bitstring_matrix_aug)" + ] + } + ], + "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-sqd/how_tos/benchmark-pauli-projection.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection.ipynb new file mode 100644 index 00000000000..127270183dd --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection.ipynb @@ -0,0 +1,482 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Benchmark Pauli operator projection\"\n", + "description: \"Benchmark Pauli operator projection for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "55900f6c-2fb6-4d2c-8745-29bad8b66d9f", + "metadata": {}, + "source": [ + "# Benchmark Pauli operator projection" + ] + }, + { + "cell_type": "markdown", + "id": "b5caa5b4", + "metadata": {}, + "source": [ + "### Pauli string action on a computational basis state\n", + "\n", + "The action of a Pauli string on a computational basis state is rather trivial, and just a single computational basis state itself. This is a direct consequence of the structure of Pauli matrices, which only have a single nonzero element on each of their rows. Consequently, their action on a qubit is:\n", + "\n", + "-------------------------------------------\n", + "\n", + "$$\n", + "\\sigma_x |0 \\rangle = |1 \\rangle\n", + "$$\n", + "\n", + "$$\n", + "\\sigma_x |1 \\rangle = |0 \\rangle\n", + "$$\n", + "\n", + "-------------------------------------------\n", + "\n", + "$$\n", + "\\sigma_y |0 \\rangle = i|1 \\rangle\n", + "$$\n", + "\n", + "$$\n", + "\\sigma_y |1 \\rangle = -i|0 \\rangle\n", + "$$\n", + "\n", + "-------------------------------------------\n", + "\n", + "$$\n", + "\\sigma_z |0 \\rangle = |0 \\rangle\n", + "$$\n", + "\n", + "$$\n", + "\\sigma_z |1 \\rangle = -|1 \\rangle\n", + "$$\n", + "\n", + "-------------------------------------------\n", + "\n", + "$$\n", + "I |0 \\rangle = |0 \\rangle\n", + "$$\n", + "\n", + "$$\n", + "I |1 \\rangle = |1 \\rangle\n", + "$$\n", + "\n", + "-------------------------------------------\n", + "\n", + "Each bit on the bitstring labeling the computational basis will be labeled by $x \\in \\{0, 1 \\}$. In order to keep the implementation at light as possible, we will\n", + "represent the bitstrings with `bool` variables: $0\\rightarrow \\textrm{False}$ and\n", + "$1\\rightarrow \\textrm{True}$.\n", + "\n", + "To represent the action of each Pauli operator in a computational basis state\n", + "we will assign three variables to it: `diag`, `sign`, `imag`.\n", + "\n", + "- `diag` labels whether the operator is diagonal:\n", + "\n", + " - $\\textrm{diag}(I) = \\textrm{True}$\n", + " - $\\textrm{diag}(\\sigma_x) = \\textrm{False}$\n", + " - $\\textrm{diag}(\\sigma_y) = \\textrm{False}$\n", + " - $\\textrm{diag}(\\sigma_z) = \\textrm{True}$\n", + "\n", + "- `sign` Identifies if there is a sign change in the matrix element connected\n", + "to either 0 or 1:\n", + "\n", + " - $\\textrm{sign}(I) = \\textrm{False}$\n", + " - $\\textrm{sign}(\\sigma_x) = \\textrm{False}$\n", + " - $\\textrm{sign}(\\sigma_y) = \\textrm{True}$\n", + " - $\\textrm{sign}(\\sigma_z) = \\textrm{True}$\n", + "\n", + "- `imag` Identifies if there is a complex component to the matrix element:\n", + "\n", + " - $\\textrm{imag}(I) = \\textrm{False}$\n", + " - $\\textrm{imag}(\\sigma_x) = \\textrm{False}$\n", + " - $\\textrm{imag}(\\sigma_y) = \\textrm{True}$\n", + " - $\\textrm{imag}(\\sigma_z) = \\textrm{False}$\n", + "\n", + "Let's label an arbitrary Pauli operator as $\\sigma \\in \\{ I, \\sigma_x, \\sigma_y\n", + "\\sigma_z\\}$. The action of the Pauli operator on a computational basis state\n", + "can then be represented by the logic operation:\n", + "$$\n", + "\\sigma |x \\rangle = |x == \\textrm{diag}(\\sigma) \\rangle (-1)^{x\\textrm{ and sign}(\\sigma)}\n", + "(i)^{\\textrm{imag}(\\sigma)}.\n", + "$$\n", + "The same is straightforwardly generalized to arbitrary number of qubits." + ] + }, + { + "cell_type": "markdown", + "id": "a8fd7e11", + "metadata": {}, + "source": [ + "Let's check that this works:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "dcb15308", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-------------------\n", + "I\n", + "|False> --> |False> ME:(1+0j)\n", + "|True> --> |True> ME:(1+0j)\n", + "-------------------\n", + "SX\n", + "|False> --> |True> ME:(1+0j)\n", + "|True> --> |False> ME:(1+0j)\n", + "-------------------\n", + "SZ\n", + "|False> --> |False> ME:(1+0j)\n", + "|True> --> |True> ME:(-1+0j)\n", + "-------------------\n", + "SY\n", + "|False> --> |True> ME:1j\n", + "|True> --> |False> ME:(-0-1j)\n" + ] + } + ], + "source": [ + "def connected_element_and_amplitude_bool(\n", + " x: bool, diag: bool, sign: bool, imag: bool\n", + ") -> tuple[bool, complex]:\n", + " \"\"\"\n", + " Finds the connected element to computational basis state |x> under\n", + " the action of the Pauli operator represented by (diag, sign, imag).\n", + "\n", + " Args:\n", + " x: Value of the bit, either True or False.\n", + " diag: Whether the Pauli operator is diagonal (I, Z)\n", + " sigma: Whether the Pauli operator's rows differ in sign (Y, Z)\n", + " imag: Whether the Pauli operator is purely imaginary (Y)\n", + "\n", + " Returns:\n", + " A length-2 tuple:\n", + " - The connected element to x, either False or True\n", + " - The matrix element\n", + " \"\"\"\n", + " return x == diag, (-1) ** (x and sign) * (1j) ** (imag)\n", + "\n", + "\n", + "sigma_indices = [0, 1, 2, 3]\n", + "sigma_string = [\"I\", \"SX\", \"SZ\", \"SY\"]\n", + "sigma_diag = [True, False, True, False]\n", + "sigma_sign = [False, False, True, True]\n", + "sigma_imag = [False, False, False, True]\n", + "qubit_values = [False, True]\n", + "\n", + "for xi in sigma_indices:\n", + " print(\"-------------------\")\n", + " print(sigma_string[xi])\n", + " for x in qubit_values:\n", + " x_p, matrix_element = connected_element_and_amplitude_bool(\n", + " x, sigma_diag[xi], sigma_sign[xi], sigma_imag[xi]\n", + " )\n", + " print(\"|\" + str(x) + \"> --> |\" + str(x_p) + \"> ME:\" + str(matrix_element))" + ] + }, + { + "cell_type": "markdown", + "id": "f40a3463", + "metadata": {}, + "source": [ + "Let's generate some large number of bitstrings (50 M) for a 40-qubit system" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "74c16c91-cc5c-46ce-aff8-17d0e71ac50f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total number of unique bitstrings: 49998839\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from qiskit_addon_sqd.qubit import sort_and_remove_duplicates\n", + "\n", + "rand_seed = 22\n", + "np.random.seed(rand_seed)\n", + "\n", + "# Generate some random bitstrings for testing\n", + "\n", + "\n", + "def random_bitstrings(n_samples, n_qubits):\n", + " return np.round(np.random.rand(n_samples, n_qubits)).astype(\"int\").astype(\"bool\")\n", + "\n", + "\n", + "n_qubits = 40\n", + "bts_matrix = random_bitstrings(50_000_000, n_qubits)\n", + "\n", + "# We need to sort the bitstrings and just keep the unique ones\n", + "# NOTE: It is essential for the projection code to have the bitstrings sorted!\n", + "bts_matrix = sort_and_remove_duplicates(bts_matrix).astype(\"bool\")\n", + "\n", + "# Final subspace dimension after getting rid of duplicated bitstrings\n", + "d = bts_matrix.shape[0]\n", + "\n", + "print(\"Total number of unique bitstrings: \" + str(d))" + ] + }, + { + "cell_type": "markdown", + "id": "184bf287", + "metadata": {}, + "source": [ + "### Benchmark SQD Pauli projection functions\n", + "\n", + "The Pauli string under consideration is $\\sigma_z \\otimes ... \\otimes \\sigma_z$.\n", + "\n", + "Different subspace dimensions are considered by just slicing the matrix of bitstrings. We time the subspace projection for the different subspace sizes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8fe182bc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration 0 took 0.201246s\n", + "Iteration 1 took 0.348222s\n", + "Iteration 2 took 0.576333s\n", + "Iteration 3 took 0.78356s\n", + "Iteration 4 took 1.016162s\n", + "Iteration 5 took 1.305325s\n", + "Iteration 6 took 1.392751s\n", + "Iteration 7 took 1.632433s\n", + "Iteration 8 took 1.826521s\n", + "Iteration 9 took 2.02903s\n", + "Iteration 10 took 2.297458s\n", + "Iteration 11 took 2.588042s\n", + "Iteration 12 took 2.738746s\n", + "Iteration 13 took 2.906144s\n", + "Iteration 14 took 3.148833s\n", + "Iteration 15 took 3.323253s\n", + "Iteration 16 took 3.664171s\n", + "Iteration 17 took 3.680663s\n", + "Iteration 18 took 4.008313s\n", + "Iteration 19 took 4.173532s\n" + ] + } + ], + "source": [ + "import time\n", + "\n", + "from qiskit.quantum_info import Pauli\n", + "from qiskit_addon_sqd.qubit import matrix_elements_from_pauli\n", + "\n", + "pauli = Pauli(\"Z\" * n_qubits)\n", + "\n", + "# Different subspace sizes to test\n", + "d_list = np.linspace(d / 1000, d, 20).astype(\"int\")\n", + "\n", + "# To store the walltime\n", + "time_array = np.zeros(20)\n", + "\n", + "for i in range(20):\n", + " int_bts_matrix = bts_matrix[: d_list[i], :]\n", + " time_1 = time.time()\n", + " _ = matrix_elements_from_pauli(int_bts_matrix, pauli)\n", + " time_array[i] = time.time() - time_1\n", + " print(f\"Iteration {i} took {round(time_array[i], 6)}s\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9abb110f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Data for energies plot\n", + "x1 = d_list\n", + "y1 = time_array\n", + "\n", + "# Plot energies\n", + "plt.title(\"Runtime vs subspace dimension 40 qubits\")\n", + "plt.xlabel(\"Subspace dimension (millions)\")\n", + "plt.ylabel(\"Wall time [s]\")\n", + "plt.xticks([1e7, 2e7, 3e7, 4e7, 5e7], [str(i) for i in [10, 20, 30, 40, 50]])\n", + "plt.plot(x1, y1, marker=\".\", markersize=20)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8c486bc7", + "metadata": {}, + "source": [ + "Let's do the same for 60 qubits" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "359ed3f3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total number of unique bitstrings: 50000000\n" + ] + } + ], + "source": [ + "n_qubits = 60\n", + "bts_matrix = random_bitstrings(50_000_000, n_qubits)\n", + "\n", + "# We need to sort the bitstrings and just keep the unique ones\n", + "bts_matrix = sort_and_remove_duplicates(bts_matrix).astype(\"bool\")\n", + "\n", + "# Final subspace dimension after getting rid of duplicated bitstrings\n", + "d = bts_matrix.shape[0]\n", + "\n", + "print(\"Total number of unique bitstrings: \" + str(d))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7bb0d8d8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration 0 took 0.236567s\n", + "Iteration 1 took 0.424116s\n", + "Iteration 2 took 0.673399s\n", + "Iteration 3 took 0.905164s\n", + "Iteration 4 took 1.168936s\n", + "Iteration 5 took 1.454204s\n", + "Iteration 6 took 1.74778s\n", + "Iteration 7 took 1.920795s\n", + "Iteration 8 took 2.259994s\n", + "Iteration 9 took 2.550674s\n", + "Iteration 10 took 2.681287s\n", + "Iteration 11 took 3.04411s\n", + "Iteration 12 took 3.293262s\n", + "Iteration 13 took 3.471247s\n", + "Iteration 14 took 3.726639s\n", + "Iteration 15 took 4.072854s\n", + "Iteration 16 took 4.221037s\n", + "Iteration 17 took 4.498535s\n", + "Iteration 18 took 4.741108s\n", + "Iteration 19 took 5.159038s\n" + ] + } + ], + "source": [ + "pauli = Pauli(\"Z\" * n_qubits)\n", + "\n", + "# Different subspace sizes to test\n", + "d_list = np.linspace(d / 1000, d, 20).astype(\"int\")\n", + "\n", + "# It is better to do this once\n", + "row_array = np.arange(d)\n", + "\n", + "# To store the walltime\n", + "time_array = np.zeros(20)\n", + "\n", + "for i in range(20):\n", + " int_bts_matrix = bts_matrix[: d_list[i], :]\n", + " int_row_array = row_array[: d_list[i]]\n", + " time_1 = time.time()\n", + " _ = matrix_elements_from_pauli(int_bts_matrix, pauli)\n", + " time_array[i] = time.time() - time_1\n", + " print(f\"Iteration {i} took {round(time_array[i], 6)}s\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6b961c81", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Data for energies plot\n", + "x1 = d_list\n", + "y1 = time_array\n", + "\n", + "fig, axs = plt.subplots(1, 1, figsize=(6, 6))\n", + "\n", + "# Plot energies\n", + "axs.plot(x1, y1, marker=\".\", markersize=20)\n", + "axs.set_title(\"Runtime vs subspace dimension 60 qubits\")\n", + "axs.set_xlabel(\"Subspace dimension (millions)\")\n", + "plt.xticks([1e7, 2e7, 3e7, 4e7, 5e7], [str(i) for i in [10, 20, 30, 40, 50]])\n", + "axs.set_ylabel(\"Wall time [s]\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "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-sqd/how_tos/choose-subspace-dimension.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension.ipynb new file mode 100644 index 00000000000..f81b5d159a3 --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension.ipynb @@ -0,0 +1,230 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Bounding the subspace dimension\"\n", + "description: \"Bounding the subspace dimension for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "9e40af77-7f0f-4dd6-ab0a-420cf396050e", + "metadata": {}, + "source": [ + "# Bounding the subspace dimension\n", + "\n", + "In this tutorial, we will show the effect of the subspace dimension in the [self-consistent configuration recovery technique](https://arxiv.org/abs/2405.05068).\n", + "\n", + "***A priori***, we do not know what is the correct subspace dimension to obtain a target level of accuracy. However, we do know that increasing the subspace dimension increases the accuracy of the method. Therefore, we can study the accuracy of the predictions as a function of the subspace dimension." + ] + }, + { + "cell_type": "markdown", + "id": "a6755afb-ca1e-4473-974b-ba89acc8abce", + "metadata": {}, + "source": [ + "Specify the molecule and its properties." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "677f54ac-b4ed-47e3-b5ba-5366d3a520f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -108.835236570775\n", + "CASCI E = -109.046671778080 E(CI) = -32.8155692383188 S^2 = 0.0000000\n" + ] + } + ], + "source": [ + "import warnings\n", + "\n", + "import pyscf\n", + "import pyscf.cc\n", + "import pyscf.mcscf\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "# Specify molecule properties\n", + "open_shell = False\n", + "spin_sq = 0\n", + "\n", + "# Build N2 molecule\n", + "mol = pyscf.gto.Mole()\n", + "mol.build(\n", + " atom=[[\"N\", (0, 0, 0)], [\"N\", (1.0, 0, 0)]],\n", + " basis=\"6-31g\",\n", + " symmetry=\"Dooh\",\n", + ")\n", + "\n", + "# Define active space\n", + "n_frozen = 2\n", + "active_space = range(n_frozen, mol.nao_nr())\n", + "\n", + "# Get molecular integrals\n", + "scf = pyscf.scf.RHF(mol).run()\n", + "num_orbitals = len(active_space)\n", + "n_electrons = int(sum(scf.mo_occ[active_space]))\n", + "num_elec_a = (n_electrons + mol.spin) // 2\n", + "num_elec_b = (n_electrons - mol.spin) // 2\n", + "cas = pyscf.mcscf.CASCI(scf, num_orbitals, (num_elec_a, num_elec_b))\n", + "mo = cas.sort_mo(active_space, base=0)\n", + "hcore, nuclear_repulsion_energy = cas.get_h1cas(mo)\n", + "eri = pyscf.ao2mo.restore(1, cas.get_h2cas(mo), num_orbitals)\n", + "\n", + "# Compute exact energy\n", + "exact_energy = cas.run().e_tot" + ] + }, + { + "cell_type": "markdown", + "id": "c58e988c-a109-44cd-a975-9df43250c318", + "metadata": {}, + "source": [ + "Generate some random bitstrings to proxy QPU samples." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e9506e0b-ed64-48bb-a97a-ef851b604af1", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit_addon_sqd.counts import generate_bit_array_uniform\n", + "\n", + "# Create a seed to control randomness throughout this workflow\n", + "rng = np.random.default_rng(24)\n", + "\n", + "# Generate random samples\n", + "bit_array = generate_bit_array_uniform(10_000, num_orbitals * 2, rand_seed=rng)" + ] + }, + { + "cell_type": "markdown", + "id": "82b062a3-d4ca-41e2-9b00-4a394f83cbdd", + "metadata": {}, + "source": [ + "Call SQD with increasing batch sizes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e0847f28", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_sqd.fermion import diagonalize_fermionic_hamiltonian\n", + "\n", + "list_samples_per_batch = [50, 200, 400, 600]\n", + "\n", + "# SQD options\n", + "max_iterations = 5\n", + "\n", + "# Eigenstate solver options\n", + "num_batches = 10\n", + "max_davidson_cycles = 200\n", + "\n", + "energies = []\n", + "subspace_dimensions = []\n", + "\n", + "for samples_per_batch in list_samples_per_batch:\n", + " result = diagonalize_fermionic_hamiltonian(\n", + " hcore,\n", + " eri,\n", + " bit_array,\n", + " samples_per_batch=samples_per_batch,\n", + " norb=num_orbitals,\n", + " nelec=(num_elec_a, num_elec_b),\n", + " num_batches=num_batches,\n", + " max_iterations=max_iterations,\n", + " symmetrize_spin=True,\n", + " seed=rng,\n", + " )\n", + " energies.append(result.energy)\n", + " subspace_dimensions.append(np.prod(result.sci_state.amplitudes.shape))" + ] + }, + { + "cell_type": "markdown", + "id": "9d78906b-4759-4506-9c69-85d4e67766b3", + "metadata": {}, + "source": [ + "This plot shows that increasing the subspace dimension leads to more accurate results." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "caffd888-e89c-4aa9-8bae-4d1bb723b35e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Data for energies plot\n", + "x1 = subspace_dimensions\n", + "y1 = np.array(energies) + nuclear_repulsion_energy\n", + "\n", + "fig, axs = plt.subplots(1, 1, figsize=(12, 6))\n", + "\n", + "# Plot energies\n", + "axs.plot(x1, y1, marker=\".\", markersize=20, label=\"Estimated\")\n", + "axs.set_xticks(x1)\n", + "axs.set_xticklabels(x1)\n", + "axs.axhline(y=exact_energy, color=\"red\", linestyle=\"--\", label=\"Exact\")\n", + "axs.set_title(\"Approximated Ground State Energy vs subspace dimension\")\n", + "axs.set_xlabel(\"Subspace dimension\")\n", + "axs.set_ylabel(\"Energy (Ha)\")\n", + "axs.legend()\n", + "\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "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-sqd/how_tos/index.mdx b/docs/addons/qiskit-addon-sqd/how_tos/index.mdx new file mode 100644 index 00000000000..661b693f3e7 --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/index.mdx @@ -0,0 +1,37 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Sample-based quantum diagonalization (SQD)" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +[![](/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg)](add-fermionic-excitations-to-configuration-pool) + +[Add fermionic transitions to the pool of configurations](add-fermionic-excitations-to-configuration-pool) + +[![](/docs/images/addons/qiskit-addon-sqd/how_tos_benchmark_pauli_projection_12_0.avif)](benchmark-pauli-projection) + +[Benchmark Pauli operator projection](benchmark-pauli-projection) + +[![](/docs/images/addons/qiskit-addon-sqd/how_tos_choose_subspace_dimension_8_0.avif)](choose-subspace-dimension) + +[Bounding the subspace dimension](choose-subspace-dimension) + +[![](/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg)](integrate-dice-solver) + +[Scale SQD chemistry workflows with Dice solver](integrate-dice-solver) + +[![](/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg)](project-pauli-operators-onto-hilbert-subspaces) + +[Project Pauli operators onto Hilbert subspaces](project-pauli-operators-onto-hilbert-subspaces) + +[![](/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg)](select-open-closed-shell) + +[Understand open-shell vs closed-shell options and its effect in the subspace construction](select-open-closed-shell) + +[![](/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg)](use-oo-to-optimize-hamiltonian-basis) + +[Optimize Hamiltonian basis with orbital optimization](use-oo-to-optimize-hamiltonian-basis) + diff --git a/docs/addons/qiskit-addon-sqd/how_tos/integrate-dice-solver.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/integrate-dice-solver.ipynb new file mode 100644 index 00000000000..0d2211a0a6f --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/integrate-dice-solver.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Scale SQD chemistry workflows with Dice solver\"\n", + "description: \"Scale SQD chemistry workflows with Dice solver for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "65adf860-4443-494f-ad90-5d8ea444ac4e", + "metadata": {}, + "source": [ + "# Scale SQD chemistry workflows with Dice solver\n", + "\n", + "For information on how to install and use ``qiskit-addon-dice-solver``, [visit the docs](https://qiskit.github.io/qiskit-addon-dice-solver/).\n", + "\n", + "For more details on the SQD code used in this example, check out [tutorial 1](https://qiskit.github.io/qiskit-addon-sqd/tutorials/01_chemistry_hamiltonian.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "baf7d074-4443-4695-875e-65f7fb8cef25", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "converged SCF energy = -108.835236570774\n", + "CASCI E = -109.046671778080 E(CI) = -32.8155692383187 S^2 = 0.0000000\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import pyscf\n", + "import pyscf.cc\n", + "import pyscf.mcscf\n", + "from qiskit_addon_dice_solver import solve_sci_batch\n", + "from qiskit_addon_sqd.counts import generate_bit_array_uniform\n", + "from qiskit_addon_sqd.fermion import diagonalize_fermionic_hamiltonian\n", + "\n", + "# Specify molecule properties\n", + "num_orbitals = 16\n", + "num_elec_a = num_elec_b = 5\n", + "spin_sq = 0\n", + "\n", + "# Build N2 molecule\n", + "mol = pyscf.gto.Mole()\n", + "mol.build(\n", + " atom=[[\"N\", (0, 0, 0)], [\"N\", (1.0, 0, 0)]],\n", + " basis=\"6-31g\",\n", + " symmetry=\"Dooh\",\n", + ")\n", + "\n", + "# Define active space\n", + "n_frozen = 2\n", + "active_space = range(n_frozen, mol.nao_nr())\n", + "\n", + "# Get molecular integrals\n", + "scf = pyscf.scf.RHF(mol).run()\n", + "num_orbitals = len(active_space)\n", + "n_electrons = int(sum(scf.mo_occ[active_space]))\n", + "num_elec_a = (n_electrons + mol.spin) // 2\n", + "num_elec_b = (n_electrons - mol.spin) // 2\n", + "cas = pyscf.mcscf.CASCI(scf, num_orbitals, (num_elec_a, num_elec_b))\n", + "mo = cas.sort_mo(active_space, base=0)\n", + "hcore, nuclear_repulsion_energy = cas.get_h1cas(mo)\n", + "eri = pyscf.ao2mo.restore(1, cas.get_h2cas(mo), num_orbitals)\n", + "\n", + "# Compute exact energy\n", + "exact_energy = cas.run().e_tot\n", + "\n", + "# Create a seed to control randomness throughout this workflow\n", + "rng = np.random.default_rng(24)\n", + "\n", + "\n", + "# Generate random samples\n", + "bit_array = generate_bit_array_uniform(10_000, num_orbitals * 2, rand_seed=rng)\n", + "\n", + "# Run SQD\n", + "result = diagonalize_fermionic_hamiltonian(\n", + " hcore,\n", + " eri,\n", + " bit_array,\n", + " samples_per_batch=300,\n", + " norb=num_orbitals,\n", + " nelec=(num_elec_a, num_elec_b),\n", + " num_batches=5,\n", + " max_iterations=5,\n", + " sci_solver=solve_sci_batch,\n", + " symmetrize_spin=True,\n", + " seed=rng,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "99709a88-f9ec-4dbc-a561-e682f90aa4c5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exact energy: -109.04667177808028\n", + "Estimated energy: -109.03402667558743\n" + ] + } + ], + "source": [ + "print(f\"Exact energy: {exact_energy}\")\n", + "print(f\"Estimated energy: {result.energy + nuclear_repulsion_energy}\")" + ] + } + ], + "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-sqd/how_tos/project-pauli-operators-onto-hilbert-subspaces.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/project-pauli-operators-onto-hilbert-subspaces.ipynb new file mode 100644 index 00000000000..1f95f501f3e --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/project-pauli-operators-onto-hilbert-subspaces.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Project Pauli operators onto Hilbert subspaces\"\n", + "description: \"Project Pauli operators onto Hilbert subspaces for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "21a1d83f-183f-4da9-bb8c-71400120fd31", + "metadata": {}, + "source": [ + "# Project Pauli operators onto Hilbert subspaces" + ] + }, + { + "cell_type": "markdown", + "id": "c961d48a", + "metadata": {}, + "source": [ + "We show different ways of projecting a weighted linear combination of pauli strings\n", + "into a subspace defined by a subset of size $d$ computational basis states. The\n", + "projected operator is stored as a $d \\times d$ ``scipy.sparse.coo_matrix``.\n", + "\n", + "As an example we consider the Hamiltonian of the 1D Heisenberg model with periodic\n", + "boundary conditions and $L = 22$ spins:\n", + "$$\n", + "H = \\sum_{\\langle i, j \\rangle}\\left( \\sigma^x_i\\sigma^x_j + \\sigma^y_i\\sigma^y_j + \\sigma^z_i\\sigma^z_j \\right).\n", + "$$\n", + "\n", + "This package provides two tools to perform this projection:\n", + "\n", + "- ``qiskit_addon_sqd.qubit.matrix_elements_from_pauli()``: is a lower-level function\n", + "that returns the non-zero matrix elements of a Pauli string and the corresponding indices\n", + "of the non-zero elements.\n", + "\n", + "- ``qiskit_addon_sqd.qubit.project_operator_to_subspace()``: is a higher-level function that\n", + "directly returns a ``scipy.sparse`` matrix.\n", + "\n", + "This notebook shows how to use these two tools to produce the same sparse operator." + ] + }, + { + "cell_type": "markdown", + "id": "6c87d5e5", + "metadata": {}, + "source": [ + "### Subspace definition\n", + "\n", + "For this example we just generate length-22 random bitstrings. For the projection\n", + "functions to work as expected, it is essential that the bitstrings that define\n", + "the subspace are unique and sorted in ascending order according to their unsigned\n", + "integer representation.\n", + "\n", + "This can be achieved with the ``qiskit_addon_sqd.qubit.sort_and_remove_duplicates()``\n", + "function." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7b94f840", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Subspace dimension: 49718\n", + "Full Hilbert space dimension: 4194304\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from qiskit_addon_sqd.qubit import sort_and_remove_duplicates\n", + "\n", + "num_spins = 22\n", + "rand_seed = 22\n", + "np.random.seed(rand_seed)\n", + "\n", + "\n", + "def random_bitstrings(n_samples, n_qubits):\n", + " return np.round(np.random.rand(n_samples, n_qubits)).astype(\"int\").astype(\"bool\")\n", + "\n", + "\n", + "bitstring_matrix = random_bitstrings(50_000, num_spins)\n", + "\n", + "# NOTE: It is essential for the projection code to have the bitstrings sorted!\n", + "bitstring_matrix = sort_and_remove_duplicates(bitstring_matrix)\n", + "\n", + "print(\"Subspace dimension: \" + str(bitstring_matrix.shape[0]))\n", + "print(\"Full Hilbert space dimension: \" + str(2**num_spins))" + ] + }, + { + "cell_type": "markdown", + "id": "5707b93c", + "metadata": {}, + "source": [ + "### First method: nonzero matrix elements and indices." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "74c16c91-cc5c-46ce-aff8-17d0e71ac50f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIYY', 'IIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIYYII', 'IIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIXXIIII', 'IIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIXXIIIIII', 'IIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIZZIIIIII', 'IIIIIIIIIIIIXXIIIIIIII', 'IIIIIIIIIIIIYYIIIIIIII', 'IIIIIIIIIIIIZZIIIIIIII', 'IIIIIIIIIIXXIIIIIIIIII', 'IIIIIIIIIIYYIIIIIIIIII', 'IIIIIIIIIIZZIIIIIIIIII', 'IIIIIIIIXXIIIIIIIIIIII', 'IIIIIIIIYYIIIIIIIIIIII', 'IIIIIIIIZZIIIIIIIIIIII', 'IIIIIIXXIIIIIIIIIIIIII', 'IIIIIIYYIIIIIIIIIIIIII', 'IIIIIIZZIIIIIIIIIIIIII', 'IIIIXXIIIIIIIIIIIIIIII', 'IIIIYYIIIIIIIIIIIIIIII', 'IIIIZZIIIIIIIIIIIIIIII', 'IIXXIIIIIIIIIIIIIIIIII', 'IIYYIIIIIIIIIIIIIIIIII', 'IIZZIIIIIIIIIIIIIIIIII', 'XXIIIIIIIIIIIIIIIIIIII', 'YYIIIIIIIIIIIIIIIIIIII', 'ZZIIIIIIIIIIIIIIIIIIII', 'XIIIIIIIIIIIIIIIIIIIIX', 'YIIIIIIIIIIIIIIIIIIIIY', 'ZIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIYYI', 'IIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIXXIIIII', 'IIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIZZIIIII', 'IIIIIIIIIIIIIXXIIIIIII', 'IIIIIIIIIIIIIYYIIIIIII', 'IIIIIIIIIIIIIZZIIIIIII', 'IIIIIIIIIIIXXIIIIIIIII', 'IIIIIIIIIIIYYIIIIIIIII', 'IIIIIIIIIIIZZIIIIIIIII', 'IIIIIIIIIXXIIIIIIIIIII', 'IIIIIIIIIYYIIIIIIIIIII', 'IIIIIIIIIZZIIIIIIIIIII', 'IIIIIIIXXIIIIIIIIIIIII', 'IIIIIIIYYIIIIIIIIIIIII', 'IIIIIIIZZIIIIIIIIIIIII', 'IIIIIXXIIIIIIIIIIIIIII', 'IIIIIYYIIIIIIIIIIIIIII', 'IIIIIZZIIIIIIIIIIIIIII', 'IIIXXIIIIIIIIIIIIIIIII', 'IIIYYIIIIIIIIIIIIIIIII', 'IIIZZIIIIIIIIIIIIIIIII', 'IXXIIIIIIIIIIIIIIIIIII', 'IYYIIIIIIIIIIIIIIIIIII', 'IZZIIIIIIIIIIIIIIIIIII'],\n", + " coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,\n", + " 1.+0.j, 1.+0.j, 1.+0.j])\n" + ] + } + ], + "source": [ + "from qiskit.transpiler import CouplingMap\n", + "from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian\n", + "\n", + "coupling_map = CouplingMap.from_ring(num_spins)\n", + "hamiltonian = generate_xyz_hamiltonian(coupling_map)\n", + "print(hamiltonian)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f31f5e40", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_sqd.qubit import matrix_elements_from_pauli\n", + "from scipy.sparse import coo_matrix\n", + "\n", + "d = bitstring_matrix.shape[0]\n", + "\n", + "# Initialize the coo_matrix object\n", + "operator_from_matrix_elements = coo_matrix((d, d), dtype=\"complex128\")\n", + "\n", + "for pauli in hamiltonian.paulis:\n", + " matrix_elements, row_indices, col_indices = matrix_elements_from_pauli(bitstring_matrix, pauli)\n", + " operator_from_matrix_elements += coo_matrix(\n", + " (matrix_elements, (row_indices, col_indices)), (d, d)\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "4479af24", + "metadata": {}, + "source": [ + "### Higher-level implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e58763df", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Projecting term 1 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIIXX ...\n", + "Projecting term 2 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIIYY ...\n", + "Projecting term 3 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIIZZ ...\n", + "Projecting term 4 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIXXII ...\n", + "Projecting term 5 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIYYII ...\n", + "Projecting term 6 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIZZII ...\n", + "Projecting term 7 out of 66: (1+0j) * IIIIIIIIIIIIIIIIXXIIII ...\n", + "Projecting term 8 out of 66: (1+0j) * IIIIIIIIIIIIIIIIYYIIII ...\n", + "Projecting term 9 out of 66: (1+0j) * IIIIIIIIIIIIIIIIZZIIII ...\n", + "Projecting term 10 out of 66: (1+0j) * IIIIIIIIIIIIIIXXIIIIII ...\n", + "Projecting term 11 out of 66: (1+0j) * IIIIIIIIIIIIIIYYIIIIII ...\n", + "Projecting term 12 out of 66: (1+0j) * IIIIIIIIIIIIIIZZIIIIII ...\n", + "Projecting term 13 out of 66: (1+0j) * IIIIIIIIIIIIXXIIIIIIII ...\n", + "Projecting term 14 out of 66: (1+0j) * IIIIIIIIIIIIYYIIIIIIII ...\n", + "Projecting term 15 out of 66: (1+0j) * IIIIIIIIIIIIZZIIIIIIII ...\n", + "Projecting term 16 out of 66: (1+0j) * IIIIIIIIIIXXIIIIIIIIII ...\n", + "Projecting term 17 out of 66: (1+0j) * IIIIIIIIIIYYIIIIIIIIII ...\n", + "Projecting term 18 out of 66: (1+0j) * IIIIIIIIIIZZIIIIIIIIII ...\n", + "Projecting term 19 out of 66: (1+0j) * IIIIIIIIXXIIIIIIIIIIII ...\n", + "Projecting term 20 out of 66: (1+0j) * IIIIIIIIYYIIIIIIIIIIII ...\n", + "Projecting term 21 out of 66: (1+0j) * IIIIIIIIZZIIIIIIIIIIII ...\n", + "Projecting term 22 out of 66: (1+0j) * IIIIIIXXIIIIIIIIIIIIII ...\n", + "Projecting term 23 out of 66: (1+0j) * IIIIIIYYIIIIIIIIIIIIII ...\n", + "Projecting term 24 out of 66: (1+0j) * IIIIIIZZIIIIIIIIIIIIII ...\n", + "Projecting term 25 out of 66: (1+0j) * IIIIXXIIIIIIIIIIIIIIII ...\n", + "Projecting term 26 out of 66: (1+0j) * IIIIYYIIIIIIIIIIIIIIII ...\n", + "Projecting term 27 out of 66: (1+0j) * IIIIZZIIIIIIIIIIIIIIII ...\n", + "Projecting term 28 out of 66: (1+0j) * IIXXIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 29 out of 66: (1+0j) * IIYYIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 30 out of 66: (1+0j) * IIZZIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 31 out of 66: (1+0j) * XXIIIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 32 out of 66: (1+0j) * YYIIIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 33 out of 66: (1+0j) * ZZIIIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 34 out of 66: (1+0j) * XIIIIIIIIIIIIIIIIIIIIX ...\n", + "Projecting term 35 out of 66: (1+0j) * YIIIIIIIIIIIIIIIIIIIIY ...\n", + "Projecting term 36 out of 66: (1+0j) * ZIIIIIIIIIIIIIIIIIIIIZ ...\n", + "Projecting term 37 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIXXI ...\n", + "Projecting term 38 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIYYI ...\n", + "Projecting term 39 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIIIZZI ...\n", + "Projecting term 40 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIXXIII ...\n", + "Projecting term 41 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIYYIII ...\n", + "Projecting term 42 out of 66: (1+0j) * IIIIIIIIIIIIIIIIIZZIII ...\n", + "Projecting term 43 out of 66: (1+0j) * IIIIIIIIIIIIIIIXXIIIII ...\n", + "Projecting term 44 out of 66: (1+0j) * IIIIIIIIIIIIIIIYYIIIII ...\n", + "Projecting term 45 out of 66: (1+0j) * IIIIIIIIIIIIIIIZZIIIII ...\n", + "Projecting term 46 out of 66: (1+0j) * IIIIIIIIIIIIIXXIIIIIII ...\n", + "Projecting term 47 out of 66: (1+0j) * IIIIIIIIIIIIIYYIIIIIII ...\n", + "Projecting term 48 out of 66: (1+0j) * IIIIIIIIIIIIIZZIIIIIII ...\n", + "Projecting term 49 out of 66: (1+0j) * IIIIIIIIIIIXXIIIIIIIII ...\n", + "Projecting term 50 out of 66: (1+0j) * IIIIIIIIIIIYYIIIIIIIII ...\n", + "Projecting term 51 out of 66: (1+0j) * IIIIIIIIIIIZZIIIIIIIII ...\n", + "Projecting term 52 out of 66: (1+0j) * IIIIIIIIIXXIIIIIIIIIII ...\n", + "Projecting term 53 out of 66: (1+0j) * IIIIIIIIIYYIIIIIIIIIII ...\n", + "Projecting term 54 out of 66: (1+0j) * IIIIIIIIIZZIIIIIIIIIII ...\n", + "Projecting term 55 out of 66: (1+0j) * IIIIIIIXXIIIIIIIIIIIII ...\n", + "Projecting term 56 out of 66: (1+0j) * IIIIIIIYYIIIIIIIIIIIII ...\n", + "Projecting term 57 out of 66: (1+0j) * IIIIIIIZZIIIIIIIIIIIII ...\n", + "Projecting term 58 out of 66: (1+0j) * IIIIIXXIIIIIIIIIIIIIII ...\n", + "Projecting term 59 out of 66: (1+0j) * IIIIIYYIIIIIIIIIIIIIII ...\n", + "Projecting term 60 out of 66: (1+0j) * IIIIIZZIIIIIIIIIIIIIII ...\n", + "Projecting term 61 out of 66: (1+0j) * IIIXXIIIIIIIIIIIIIIIII ...\n", + "Projecting term 62 out of 66: (1+0j) * IIIYYIIIIIIIIIIIIIIIII ...\n", + "Projecting term 63 out of 66: (1+0j) * IIIZZIIIIIIIIIIIIIIIII ...\n", + "Projecting term 64 out of 66: (1+0j) * IXXIIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 65 out of 66: (1+0j) * IYYIIIIIIIIIIIIIIIIIII ...\n", + "Projecting term 66 out of 66: (1+0j) * IZZIIIIIIIIIIIIIIIIIII ...\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.qubit import project_operator_to_subspace\n", + "\n", + "operator = project_operator_to_subspace(bitstring_matrix, hamiltonian, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "1fce97a2", + "metadata": {}, + "source": [ + "Check that both implementations yield the same coo_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4bf56509", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0j\n" + ] + } + ], + "source": [ + "print((operator.power(2) - operator_from_matrix_elements.power(2)).sum())" + ] + } + ], + "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-sqd/how_tos/select-open-closed-shell.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/select-open-closed-shell.ipynb new file mode 100644 index 00000000000..3040d86380c --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/select-open-closed-shell.ipynb @@ -0,0 +1,542 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Understand open-shell vs closed-shell options and its effect in the subspace construction\"\n", + "description: \"Understand open-shell vs closed-shell options and its effect in the subspace construction for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "72420c62-2716-4f64-ad5b-73394b481cc1", + "metadata": {}, + "source": [ + "# Understand open-shell vs closed-shell options and its effect in the subspace construction\n", + "\n", + "In this \"how-to\", we will show how to choose subpace dimensions in the `qiskit_addon_sqd` package to post-process quantum samples using the [self-consistent configuration recovery technique](https://arxiv.org/abs/2405.05068).\n", + "\n", + "More importantly, this \"how-to\" also highlights some differences in the behaviour in the susbapce construction when run in `open_shell = False` or `open_shell = True` modes:\n", + "\n", + "- `open_shell = False` only works when the number of spin-up and spin-down electrons is the same.\n", + "\n", + "- `open_shell = True` must be used when the number of spin-up and spin-down electrons is different. It can also be used when the number of spin-up and spin-down electrons is the same. However, in this last case, there is a difference in the sizes of the subspaces generated between `open_shell = False` and `open_shell = True`, as discussed in this notebook.\n", + "\n", + "**NOTE:** Some of the electronic-configuration (bitstring) manipulations in this package have as a goal to preserve the total spin symmetry $S^2$. Standard Selected Counfiguration Interaction (SCI) solvers cannot impose $S^2$ conservation exactly. Consequently, they do so approximately via a Lagrange multiplier.\n", + "\n", + "The choice of electronic configurations entering the eigenstate solver can also have a strong effect in the conservation of spin. For example, in a (2-electron,2-orbital) system, one may sample the configuration $|1001\\rangle$ (having a single spin-up excitation over the RHF state $|0101\\rangle$) which is a linear combination of the open-shell singlet and triplet states, respectively $(|1001\\rangle ± |0110\\rangle) /\\sqrt{2}$. If the configuration |0110⟩ is not sampled, one can construct neither eigenfunction of total spin, leading to spin contamination or redundancy (i.e. the configuration |1001⟩ is involved in a CI calculation, but has coefficient 0 in the CI vector). Consider that a single sample $|1001\\rangle$ is generated in the quantum computer, this is how the `sqd` package handles this situation:\n", + "\n", + "- `open_shell = False`:\n", + "\n", + " 1. The $1001$ bitstring is split in half, representing spin-up and spin-down configurations: $10$ (up) and $01$ (down).\n", + "\n", + " 2. The list of unique spin-polarized configurations is constructed: $\\mathcal{U} = [01, 10]$.\n", + "\n", + " 3. We then consider all possible combinations of $\\mathcal{U}$ elements to form the basis: $\\left \\{ |0101\\rangle, |0110\\rangle , |1001\\rangle , |1010\\rangle \\right \\}$, which contains the singlet and triplet states.\n", + "\n", + "\n", + "- `open_shell = True`:\n", + "\n", + " 1. Contrary to the `open_shell = False` case, we do not combine the halves of the bitstring to form the basis." + ] + }, + { + "cell_type": "markdown", + "id": "a6755afb-ca1e-4473-974b-ba89acc8abce", + "metadata": {}, + "source": [ + "### Closed-Shell\n", + "\n", + "This example shows how the bitstrings are manipulated in a (2-electron, 4-orbital) system." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "677f54ac-b4ed-47e3-b5ba-5366d3a520f9", + "metadata": {}, + "outputs": [], + "source": [ + "# Specify molecule properties\n", + "num_orbitals = 4\n", + "num_elec_a = num_elec_b = 1\n", + "open_shell = False" + ] + }, + { + "cell_type": "markdown", + "id": "c58e988c-a109-44cd-a975-9df43250c318", + "metadata": {}, + "source": [ + "Specify by hand a dictionary of measurement outcomes" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e9506e0b-ed64-48bb-a97a-ef851b604af1", + "metadata": {}, + "outputs": [], + "source": [ + "counts_dict = {\"00010010\": 1 / 2.0 - 0.01, \"01001000\": 1 / 2.0 - 0.01, \"00010001\": 0.02}" + ] + }, + { + "cell_type": "markdown", + "id": "851bc98e-9c08-4e78-9472-36301abc11d8", + "metadata": {}, + "source": [ + "Transform the counts dict into a bitstring matrix and probability array for post-processing" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7a102a7f-aae6-4583-ab82-ae40fcb5496a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[False False False True False False True False]\n", + " [False True False False True False False False]\n", + " [False False False True False False False True]]\n", + "[0.49 0.49 0.02]\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.counts import counts_to_arrays\n", + "\n", + "# Convert counts into bitstring and probability arrays\n", + "bitstring_matrix_full, probs_arr_full = counts_to_arrays(counts_dict)\n", + "print(bitstring_matrix_full)\n", + "print(probs_arr_full)" + ] + }, + { + "cell_type": "markdown", + "id": "eb704101-0fe8-4d12-b572-b1d844e35a90", + "metadata": {}, + "source": [ + "Subsample a single batch of size two:\n", + "\n", + "- ``n_batches = 1``: Number of batches of configurations used by the different calls to the eigenstate solver\n", + "- ``samples_per_batch = 2``: Number of unique configurations to include in each batch" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fe60aee2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[False False False True False False True False]\n", + " [False True False False True False False False]]\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.subsampling import postselect_and_subsample\n", + "\n", + "n_batches = 1\n", + "samples_per_batch = 2\n", + "\n", + "# seed for random number generator\n", + "rand_seed = 48\n", + "\n", + "# Generate the batches\n", + "batches = postselect_and_subsample(\n", + " bitstring_matrix_full,\n", + " probs_arr_full,\n", + " hamming_right=num_elec_a,\n", + " hamming_left=num_elec_b,\n", + " samples_per_batch=samples_per_batch,\n", + " num_batches=n_batches,\n", + " rand_seed=rand_seed,\n", + ")\n", + "\n", + "print(batches[0])" + ] + }, + { + "cell_type": "markdown", + "id": "93a6d05a", + "metadata": {}, + "source": [ + "Obtain decimal representation of the spin-up and spin-down bitstrings used by the eigenstate solver\n", + "\n", + "The fist element in the tuple corresponds to the decimal representation of the spin-up configurations, while the second element in the tuple corresponds to the decimal representation of the spin-down configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ef90e039", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(array([1, 2, 4, 8], dtype=int64), array([1, 2, 4, 8], dtype=int64))\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.fermion import bitstring_matrix_to_ci_strs\n", + "\n", + "ci_strs = bitstring_matrix_to_ci_strs(batches[0], open_shell=open_shell)\n", + "print(ci_strs)" + ] + }, + { + "cell_type": "markdown", + "id": "70d7883c", + "metadata": {}, + "source": [ + "Note that while the number of samples per batch is 2, and the sampled bitstrings are: $00010010$ and $01001000$, four electronic configurations are generated per spin-species. In this case, the set of unique spin-polarized configurations is given by:\n", + "$$\n", + "\\mathcal{U} = \\{ 0001, 0010, 0100, 1000 \\}\n", + "$$\n", + "whose base-10 decimal representation is\n", + "$$\n", + "\\mathcal{U}_{10} = \\{ 1, 2, 4, 8 \\}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "03b32ed5", + "metadata": {}, + "source": [ + "Basis of the subspace:\n", + "\n", + "The eigenstate solver takes all possible pairs of spin-up and spin-down bitstrings to construnct the basis $\\mathcal{B}$ of the subspace:\n", + "\n", + "- Element 1: $|00010001\\rangle$\n", + "\n", + "- Element 2: $|00010010\\rangle$\n", + "\n", + "- Element 3: $|00010100\\rangle$\n", + "\n", + "- Element 4: $|00011000\\rangle$\n", + "\n", + "- Element 5: $|00100001\\rangle$\n", + "\n", + "- Element 6: $|00100010\\rangle$\n", + "\n", + "- Element 7: $|00100100\\rangle$\n", + "\n", + "- Element 8: $|00101000\\rangle$\n", + "\n", + "- Element 9: $|01000001\\rangle$\n", + "\n", + "- Element 10: $|01000010\\rangle$\n", + "\n", + "- Element 11: $|01000100\\rangle$\n", + "\n", + "- Element 12: $|01001000\\rangle$\n", + "\n", + "- Element 13: $|10000001\\rangle$\n", + "\n", + "- Element 14: $|10000010\\rangle$\n", + "\n", + "- Element 15: $|10000100\\rangle$\n", + "\n", + "- Element 16: $|10001000\\rangle$" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "11c924ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Basis elements of the subspace:\n", + "|00010001>\n", + "|00010010>\n", + "|00010100>\n", + "|00011000>\n", + "|00100001>\n", + "|00100010>\n", + "|00100100>\n", + "|00101000>\n", + "|01000001>\n", + "|01000010>\n", + "|01000100>\n", + "|01001000>\n", + "|10000001>\n", + "|10000010>\n", + "|10000100>\n", + "|10001000>\n" + ] + } + ], + "source": [ + "ci_strs_up, ci_strs_dn = ci_strs\n", + "\n", + "print(\"Basis elements of the subspace:\")\n", + "\n", + "for ci_str_up in ci_strs_up:\n", + " for ci_str_dn in ci_strs_dn:\n", + " format_name = \"{0:0\" + str(num_orbitals) + \"b}\"\n", + " print(\"|\" + format_name.format(ci_str_up) + format_name.format(ci_str_dn) + \">\")" + ] + }, + { + "cell_type": "markdown", + "id": "aa43a4fc", + "metadata": {}, + "source": [ + "**The subspace dimension is upper-bounded by**: $2 \\cdot$ (`samples_per_batch`)$^2$" + ] + }, + { + "cell_type": "markdown", + "id": "9412e52b", + "metadata": {}, + "source": [ + "### Open-Shell\n", + "\n", + "This example shows how the bitstrings are manipulated in a (2-electron, 4-orbital) system." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9b515b8a", + "metadata": {}, + "outputs": [], + "source": [ + "# Specify molecule properties\n", + "num_orbitals = 4\n", + "num_elec_a = num_elec_b = 1\n", + "open_shell = True" + ] + }, + { + "cell_type": "markdown", + "id": "9ef78560", + "metadata": {}, + "source": [ + "Specify by hand a dictionary of measurement outcomes" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "06b2185a", + "metadata": {}, + "outputs": [], + "source": [ + "counts_dict = {\"00010010\": 1 / 2.0 - 0.01, \"01001000\": 1 / 2.0 - 0.01, \"00010001\": 0.02}" + ] + }, + { + "cell_type": "markdown", + "id": "08b32957", + "metadata": {}, + "source": [ + "Transform the counts dict into a bitstring matrix and probability array for post-processing" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "90561893", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[False False False True False False True False]\n", + " [False True False False True False False False]\n", + " [False False False True False False False True]]\n", + "[0.49 0.49 0.02]\n" + ] + } + ], + "source": [ + "# Convert counts into bitstring and probability arrays\n", + "bitstring_matrix_full, probs_arr_full = counts_to_arrays(counts_dict)\n", + "print(bitstring_matrix_full)\n", + "print(probs_arr_full)" + ] + }, + { + "cell_type": "markdown", + "id": "416bfb6c", + "metadata": {}, + "source": [ + "Subsample a single batch of size two:\n", + "\n", + "- ``n_batches = 1``: Number of batches of configurations used by the different calls to the eigenstate solver\n", + "- ``samples_per_batch = 2``: Number of unique configurations to include in each batch" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "cf4fe11d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[False False False True False False True False]\n", + " [False True False False True False False False]]\n" + ] + } + ], + "source": [ + "n_batches = 1\n", + "samples_per_batch = 2\n", + "\n", + "# seed for random number generator\n", + "rand_seed = 48\n", + "\n", + "# Generate the batches\n", + "batches = postselect_and_subsample(\n", + " bitstring_matrix_full,\n", + " probs_arr_full,\n", + " hamming_right=num_elec_a,\n", + " hamming_left=num_elec_b,\n", + " samples_per_batch=samples_per_batch,\n", + " num_batches=n_batches,\n", + " rand_seed=rand_seed,\n", + ")\n", + "\n", + "print(batches[0])" + ] + }, + { + "cell_type": "markdown", + "id": "54d699ca", + "metadata": {}, + "source": [ + "Obtain decimal representation of the spin-up and spin-down bitstrings used by the eigenstate solver\n", + "\n", + "The fist element in the tuple corresponds to the decimal representation of the spin-up configurations, while the second element in the tuple corresponds to the decimal representation of the spin-down configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b40b049b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(array([2, 8], dtype=int64), array([1, 4], dtype=int64))\n" + ] + } + ], + "source": [ + "ci_strs = bitstring_matrix_to_ci_strs(batches[0], open_shell=open_shell)\n", + "print(ci_strs)" + ] + }, + { + "cell_type": "markdown", + "id": "28921e56", + "metadata": {}, + "source": [ + "If we specify that `open_shell = True`, now we do not include all unique half-bitstrings as spin-up and spin-down configurations, thus yielding a smaller basis as when specifying `open_shell = False`" + ] + }, + { + "cell_type": "markdown", + "id": "e1959b72", + "metadata": {}, + "source": [ + "Basis of the subspace:\n", + "\n", + "The eigenstate solver takes all possible pairs of spin-up and spin-down bitstrings to construnct the basis $\\mathcal{B}$ of the subspace:\n", + "\n", + "- Element 1: $|00010010\\rangle$\n", + "\n", + "- Element 2: $|00011000\\rangle$\n", + "\n", + "- Element 3: $|01000010\\rangle$\n", + "\n", + "- Element 4: $|01001000\\rangle$" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a550aba2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Basis elements of the subspace:\n", + "|00100001>\n", + "|00100100>\n", + "|10000001>\n", + "|10000100>\n" + ] + } + ], + "source": [ + "ci_strs_up, ci_strs_dn = ci_strs\n", + "\n", + "print(\"Basis elements of the subspace:\")\n", + "\n", + "for ci_str_up in ci_strs_up:\n", + " for ci_str_dn in ci_strs_dn:\n", + " format_name = \"{0:0\" + str(num_orbitals) + \"b}\"\n", + " print(\"|\" + format_name.format(ci_str_up) + format_name.format(ci_str_dn) + \">\")" + ] + }, + { + "cell_type": "markdown", + "id": "7317bc49", + "metadata": {}, + "source": [ + "**The subspace dimension is upper-bounded by**: (`samples_per_batch`)$^2$" + ] + } + ], + "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-sqd/how_tos/use-oo-to-optimize-hamiltonian-basis.ipynb b/docs/addons/qiskit-addon-sqd/how_tos/use-oo-to-optimize-hamiltonian-basis.ipynb new file mode 100644 index 00000000000..4f2fb162fda --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/how_tos/use-oo-to-optimize-hamiltonian-basis.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Optimize Hamiltonian basis with orbital optimization\"\n", + "description: \"Optimize Hamiltonian basis with orbital optimization for the latest version of Sample-based quantum diagonalization (SQD)\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "9e40af77-7f0f-4dd6-ab0a-420cf396050e", + "metadata": {}, + "source": [ + "# Optimize Hamiltonian basis with orbital optimization\n", + "\n", + "In this tutorial, we will show how to use the `sqd` package to post-process quantum samples using the [self-consistent configuration recovery technique](https://arxiv.org/abs/2405.05068) and then further optimize the ground state approximation using orbital optimization\n", + "\n", + "Refer to [Sec. II A 4](https://arxiv.org/pdf/2405.05068) for a more detailed discussion on this technique." + ] + }, + { + "cell_type": "markdown", + "id": "a6755afb-ca1e-4473-974b-ba89acc8abce", + "metadata": {}, + "source": [ + "### Specify the molecule and generate samples\n", + "\n", + "In this example, we will approximate the ground state energy of an $N_2$ molecule and then improve the answer using orbital optimization. This guide studies $N_2$ at equilibrium, which is mean-field dominated. This means the MO basis is already a good choice for our integrals; therefore, we will rotate our integrals **out** of the MO basis in order to illustrate the effects of orbital optimization." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "677f54ac-b4ed-47e3-b5ba-5366d3a520f9", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "import numpy as np\n", + "import pyscf\n", + "import pyscf.cc\n", + "import pyscf.mcscf\n", + "from qiskit_addon_sqd.fermion import rotate_integrals\n", + "\n", + "# Specify molecule properties\n", + "open_shell = False\n", + "spin_sq = 0\n", + "\n", + "# Build N2 molecule\n", + "mol = pyscf.gto.Mole()\n", + "mol.build(\n", + " atom=[[\"N\", (0, 0, 0)], [\"N\", (1.0, 0, 0)]],\n", + " basis=\"6-31g\",\n", + " symmetry=\"Dooh\",\n", + ")\n", + "\n", + "# Define active space\n", + "n_frozen = 2\n", + "active_space = range(n_frozen, mol.nao_nr())\n", + "\n", + "# Get molecular integrals\n", + "scf = pyscf.scf.RHF(mol).run()\n", + "num_orbitals = len(active_space)\n", + "n_electrons = int(sum(scf.mo_occ[active_space]))\n", + "num_elec_a = (n_electrons + mol.spin) // 2\n", + "num_elec_b = (n_electrons - mol.spin) // 2\n", + "cas = pyscf.mcscf.CASCI(scf, num_orbitals, (num_elec_a, num_elec_b))\n", + "mo = cas.sort_mo(active_space, base=0)\n", + "hcore, nuclear_repulsion_energy = cas.get_h1cas(mo)\n", + "eri = pyscf.ao2mo.restore(1, cas.get_h2cas(mo), num_orbitals)\n", + "\n", + "# Compute exact energy\n", + "exact_energy = cas.run().e_tot" + ] + }, + { + "cell_type": "markdown", + "id": "b23a74fc-708c-4bd0-af4e-c9cd189b6346", + "metadata": {}, + "source": [ + "The MO basis is already a good basis for this problem, so we will rotate out of that basis in this guide in order to highlight the effect of orbital optimization." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9f46d1c6-3b49-45ad-b9ed-972bd58f7e3c", + "metadata": {}, + "outputs": [], + "source": [ + "# Rotate our integrals out of MO basis\n", + "rng = np.random.default_rng(24)\n", + "num_params = (num_orbitals**2 - num_orbitals) // 2 # antisymmetric, specified by upper triangle\n", + "k_rot = (rng.random(num_params) - 0.5) * 0.5\n", + "hcore_rot, eri_rot = rotate_integrals(hcore, eri, k_rot)" + ] + }, + { + "cell_type": "markdown", + "id": "c58e988c-a109-44cd-a975-9df43250c318", + "metadata": {}, + "source": [ + "Generate samples" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e9506e0b-ed64-48bb-a97a-ef851b604af1", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_sqd.counts import counts_to_arrays, generate_counts_uniform\n", + "\n", + "# Generate random samples\n", + "counts_dict = generate_counts_uniform(10_000, num_orbitals * 2, rand_seed=rng)\n", + "\n", + "# Convert counts into bitstring and probability arrays\n", + "bitstring_matrix_full, probs_arr_full = counts_to_arrays(counts_dict)" + ] + }, + { + "cell_type": "markdown", + "id": "eb704101-0fe8-4d12-b572-b1d844e35a90", + "metadata": {}, + "source": [ + "### Iteratively refine the samples using SQD and approximate the ground state" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b72c048e-fe8e-4fc2-b28b-03138249074e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting configuration recovery iteration 0\n", + "Starting configuration recovery iteration 1\n", + "Starting configuration recovery iteration 2\n", + "Starting configuration recovery iteration 3\n", + "Starting configuration recovery iteration 4\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.configuration_recovery import recover_configurations\n", + "from qiskit_addon_sqd.fermion import solve_fermion\n", + "from qiskit_addon_sqd.subsampling import postselect_and_subsample\n", + "\n", + "# SQSD options\n", + "iterations = 5\n", + "\n", + "# Eigenstate solver options\n", + "n_batches = 3\n", + "samples_per_batch = 100\n", + "max_davidson_cycles = 200\n", + "\n", + "# Self-consistent configuration recovery loop\n", + "e_hist = np.zeros((iterations, n_batches)) # energy history\n", + "s_hist = np.zeros((iterations, n_batches)) # spin history\n", + "occupancy_hist = []\n", + "avg_occupancy = None\n", + "for i in range(iterations):\n", + " print(f\"Starting configuration recovery iteration {i}\")\n", + " # On the first iteration, we have no orbital occupancy information from the\n", + " # solver, so we just post-select from the full bitstring set based on hamming weight.\n", + " if avg_occupancy is None:\n", + " bs_mat_tmp = bitstring_matrix_full\n", + " probs_arr_tmp = probs_arr_full\n", + "\n", + " # In following iterations, we use both the occupancy info and the target hamming\n", + " # weight to refine bitstrings.\n", + " else:\n", + " bs_mat_tmp, probs_arr_tmp = recover_configurations(\n", + " bitstring_matrix_full,\n", + " probs_arr_full,\n", + " avg_occupancy,\n", + " num_elec_a,\n", + " num_elec_b,\n", + " rand_seed=rng,\n", + " )\n", + "\n", + " # Throw out samples with incorrect hamming weight and create batches of subsamples.\n", + " batches = postselect_and_subsample(\n", + " bs_mat_tmp,\n", + " probs_arr_tmp,\n", + " hamming_right=num_elec_a,\n", + " hamming_left=num_elec_b,\n", + " samples_per_batch=samples_per_batch,\n", + " num_batches=n_batches,\n", + " rand_seed=rng,\n", + " )\n", + "\n", + " # Run eigenstate solvers in a loop. This loop should be parallelized for larger problems.\n", + " int_e = np.zeros(n_batches)\n", + " int_s = np.zeros(n_batches)\n", + " int_occs = []\n", + " cs = []\n", + " for j in range(n_batches):\n", + " energy_sci, coeffs_sci, avg_occs, spin = solve_fermion(\n", + " batches[j],\n", + " hcore_rot,\n", + " eri_rot,\n", + " open_shell=open_shell,\n", + " spin_sq=spin_sq,\n", + " max_cycle=max_davidson_cycles,\n", + " )\n", + " energy_sci += nuclear_repulsion_energy\n", + " int_e[j] = energy_sci\n", + " int_s[j] = spin\n", + " int_occs.append(avg_occs)\n", + " cs.append(coeffs_sci)\n", + "\n", + " # Combine batch results\n", + " avg_occupancy = tuple(np.mean(int_occs, axis=0))\n", + "\n", + " # Track optimization history\n", + " e_hist[i, :] = int_e\n", + " s_hist[i, :] = int_s\n", + " occupancy_hist.append(avg_occupancy)" + ] + }, + { + "cell_type": "markdown", + "id": "917cf2d0", + "metadata": {}, + "source": [ + "### Refine the subspace\n", + "\n", + "To refine the subspace, we will take the CI strings of the batch with the lowest energy\n", + "from the last configuration recovery step. Other strategies may be used, like taking the union\n", + "of the CI strings of the batches in the last configuration recovery iteration." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2a587030", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Subspace dimension: 32761\n", + "Energy of that batch from SQD: -108.7531706601421\n" + ] + } + ], + "source": [ + "from qiskit_addon_sqd.fermion import bitstring_matrix_to_ci_strs\n", + "\n", + "best_batch = batches[np.argmin(e_hist[-1])]\n", + "ci_strs_up, ci_strs_dn = bitstring_matrix_to_ci_strs(best_batch, open_shell=open_shell)\n", + "print(f\"Subspace dimension: {len(ci_strs_up) * len(ci_strs_dn)}\")\n", + "print(f\"Energy of that batch from SQD: {e_hist[-1, np.argmin(e_hist[-1])]}\")\n", + "\n", + "# Union strategy\n", + "\n", + "# batches_union = np.concatenate((batches[0], batches[1]), axis = 0)\n", + "# for i in range(n_batches-2):\n", + "# batches_union = np.concatenate((batches_union, batches[ i+ 2]))\n", + "# ci_strs_up, ci_strs_dn = bitstring_matrix_to_ci_strs(\n", + "# batches_union, open_shell=open_shell\n", + "# )\n", + "# print (f\"Subspace dimension: {len(ci_strs_up) * len(ci_strs_dn)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e8c6d5e4", + "metadata": {}, + "source": [ + "### Perform orbital optimization to improve the energy approximation\n", + "\n", + "We now describe how to optimize the orbitals to further improve the quality of the sqd calculation.\n", + "\n", + "The orbital rotations that are implemented in this package are those described by:\n", + "$$\n", + "U(\\kappa) = e^{\\sum_{pq, \\sigma} \\kappa_{pq} c^\\dagger_{p\\sigma} c_{q\\sigma}},\n", + "$$\n", + "where $\\kappa_{p, q} \\in \\mathbb{R}$ and $\\kappa_{p, q} = -\\kappa_{q, p}$. The orbitals are optimized to\n", + "minimize the variational energy:\n", + "$$\n", + "E(\\kappa) = \\langle \\psi | U^\\dagger(\\kappa) H U(\\kappa) |\\psi \\rangle,\n", + "$$\n", + "with respect to $\\kappa$ using gradient descent with momentum. Recall that\n", + "$|\\psi\\rangle$ is spanned in a subspace defined by determinants.\n", + "\n", + "Since the change of basis alters the Hamiltonian, we allow $|\\psi\\rangle$ to\n", + "respond to the change in the Hamiltonian. This is done by performing a number of alternating\n", + "self-consistent optimizations of $\\kappa$ and $|\\psi\\rangle$. We recall that the optimal\n", + "$|\\psi\\rangle$ is given by the lowest eigenvector of the Hamiltonian projected into the\n", + "subspace.\n", + "\n", + "The ``sqd.fermion.fermion`` module provides the tools to perform this alternating\n", + "optimization. In particular, the function ``sqd.fermion.optimize_orbitals()``.\n", + "\n", + "Some of the arguments that define the optimization are:\n", + "\n", + "- ``num_iters``: number of self-consistent iterations.\n", + "- ``num_steps_grad``: number of gradient step updates performed when optimizing\n", + "$\\kappa$ on each self-consistent iteration.\n", + "- ``learning_rate``: step-size in the gradient descent optimization of $\\kappa$." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b5e56baf", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_addon_sqd.fermion import optimize_orbitals\n", + "\n", + "k_flat = (rng.random(num_params) - 0.5) * 0.1\n", + "num_iters = 20\n", + "num_steps_grad = 10_000 # relatively cheap to execute\n", + "learning_rate = 0.05\n", + "\n", + "e_improved, k_flat, orbital_occupancies = optimize_orbitals(\n", + " best_batch,\n", + " hcore_rot,\n", + " eri_rot,\n", + " k_flat,\n", + " open_shell=open_shell,\n", + " spin_sq=spin_sq,\n", + " num_iters=num_iters,\n", + " num_steps_grad=num_steps_grad,\n", + " learning_rate=learning_rate,\n", + " max_cycle=max_davidson_cycles,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e06f5c28-83d0-4dc2-b2bd-2ec92676745d", + "metadata": {}, + "source": [ + "Here we see that by optimizing rotation parameters for our Hamiltonian, we can improve the result from SQD." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "78a80e64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exact energy: -109.04667177808032\n", + "SQD energy: -108.7531706601421\n", + "Energy after OO: -108.80400806164377\n" + ] + } + ], + "source": [ + "print(f\"Exact energy: {exact_energy}\")\n", + "print(f\"SQD energy: {np.min(e_hist[-1])}\")\n", + "print(f\"Energy after OO: {e_improved + nuclear_repulsion_energy}\")" + ] + } + ], + "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-sqd/index.mdx b/docs/addons/qiskit-addon-sqd/index.mdx new file mode 100644 index 00000000000..5cf2ce317d1 --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/index.mdx @@ -0,0 +1,104 @@ +--- +title: "Qiskit addon: sample-based quantum diagonalization (SQD)" +description: "Documentation for the latest version of Sample-based quantum diagonalization (SQD)" +--- + +# Qiskit addon: sample-based quantum diagonalization (SQD) + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package contains the Qiskit addon for sample-based quantum diagonalization (SQD) – a technique for finding eigenvalues and eigenvectors of quantum operators, such as a quantum system Hamiltonian, using quantum and distributed classical computing together \[1-5]. This technique can be run on current quantum computers and has been shown to scale to problem sizes beyond what was possible with variational methods and even beyond the reach of exact classical diagonalization methods \[1,2]. + +SQD-based workflows involve first preparing one or more quantum states on a quantum device and sampling from them. Then, classical distributed computing is used to process those noisy samples. This processing occurs iteratively in two steps: First, a configuration recovery step corrects noisy samples using information about the input problem and second, the Hamiltonian is projected and diagonalized in the subspace spanned by those samples. These steps are repeated self-consistently until convergence. The result is an approximated lowest eigenvalue (energy) and lowest energy eigenstate of a given Hamiltonian. SQD is robust to samples corrupted by quantum noise; in fact, as long as a useful signal can be retrieved out of the quantum computer, the outcome of SQD will be insensitive to noisy bitstrings. + +SQD can be used in various ways in practice. For example, we can use two categories of quantum circuits to sample from: + +> 1. A variational circuit ansatz with parameters chosen such that sampling the circuit produces electronic configurations on which the target wavefunction (i.e., the ground state) has significant support. This is appealing for chemistry applications where Hamiltonians can have millions of interaction terms \[1]. For an example of this approach applied to chemistry see the [tutorial for approximating the ground state energy of the N2 molecule](/docs/en/tutorials/sample-based-quantum-diagonalization). +> 2. A set of Krylov basis states are prepared over increasing time intervals. Assuming a good initial state and sparsity of the ground state, this approach is proven to converge efficiently. As one needs to prepare Trotterized time evolution circuits on a quantum device, this approach is best for applications to lattice models \[2]. For an example of this approach applied to Fermionic lattice Hamiltonians, see the [tutorial for approximating the ground state energy of a simplified single-impurity Anderson model](/docs/en/tutorials/sample-based-krylov-quantum-diagonalization). + +This package contains the functionality for the classical processing of user-provided samples. It can target Hamiltonians expressed as linear combinations of Pauli operators or second-quantized Fermionic operators. The projection and diagonalization steps are performed by a classical solver. We provide here two generic solvers, one for Fermionic systems and another for qubit systems. Other solvers that might be more efficient for specific systems can be interfaced by the users. + +## Documentation + +All documentation is available [here](https://qiskit.github.io/qiskit-addon-sqd/). + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-sqd' +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## System sizes and computational requirements + +The computational cost of SQD is dominated by the eigenstate solver calls. At each step of the self-consistent configuration recovery iteration, n\_batches of eigenstate solver calls are performed. The different calls are embarrassingly parallel. In this [tutorial](tutorials/01-chemistry-hamiltonian), those calls are inside a for loop. **It is highly recommended to perform these calls in parallel**. + +The [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#qiskit_addon_sqd.fermion.solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") function is multithreaded and capable of handling systems with \~25 spacial orbitals and \~10 electrons with subspace dimensions of \~\$10^7\$, using \~10-30 cores. + +## Choosing subspace dimensions + +The choice of the subspace dimension affects the accuracy and runtime of the eigenstate solver. The larger the subspace the more accurate the calculation, at the cost of increasing the runtime and memory requirements. The optimal subspace size of a given system is not known, thus a convergence study with the subspace dimension may be performed, as described in this [guide](how_tos/choose-subspace-dimension). + +## The subspace dimension is set indirectly + +In this package, the user controls the number of bitstrings contained in each subspace with the samples\_per\_batch argument in [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](/docs/api/qiskit-addon-sqd/subsampling#qiskit_addon_sqd.subsampling.postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample"). The value of this argument sets an upper bound to the subspace dimension in the case of quantum chemistry applications. See this [example](how_tos/select-open-closed-shell) for more details. + +## Citing this project + +If you use this package in your research, please cite it according to `CITATON.bib` file included in this repository: + +```bibtex +@software{qiskit-addon-sqd, + author = { + Abdullah Ash Saki + and Stefano Barison + and Bryce Fuller + and James R. Garrison + and Jennifer R. Glick + and Caleb Johnson + and Antonio Mezzacapo + and Javier Robledo-Moreno + and Max Rossmannek + and Paul Schweigert + and Iskandar Sitdikov + and Kevin J. Sung + }, + title = {{Qiskit addon: sample-based quantum diagonalization}}, + howpublished = {\url{https://github.com/Qiskit/qiskit-addon-sqd}}, + year = {2024} +} +``` + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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. + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-sqd). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-sqd/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-sqd/issues/new/choose) for tracking requests and bugs. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-sqd/blob/main/LICENSE.txt) + + + +## References + +\[1] Javier Robledo-Moreno, et al., \[Chemistry Beyond Exact Solutions on a Quantum-Centric Supercomputer]\([https://arxiv.org/abs/2405.05068](https://arxiv.org/abs/2405.05068)), arXiv:2405.05068 \[quant-ph]. + +\[2] Jeffery Yu, et al., \[Quantum-Centric Algorithm for Sample-Based Krylov Diagonalization]\([https://arxiv.org/abs/2501.09702](https://arxiv.org/abs/2501.09702)), arXiv:2501.09702 \[quant-ph]. + +\[3] Keita Kanno, et al., \[Quantum-Selected Configuration Interaction: classical diagonalization of Hamiltonians in subspaces selected by quantum computers]\([https://arxiv.org/abs/2302.11320](https://arxiv.org/abs/2302.11320)), arXiv:2302.11320 \[quant-ph]. + +\[4] Kenji Sugisaki, et al., \[Hamiltonian simulation-based quantum-selected configuration interaction for large-scale electronic structure calculations with a quantum computer]\([https://arxiv.org/abs/2412.07218](https://arxiv.org/abs/2412.07218)), arXiv:2412.07218 \[quant-ph]. + +\[5] Mathias Mikkelsen, Yuya O. Nakagawa, \[Quantum-selected configuration interaction with time-evolved state]\([https://arxiv.org/abs/2412.13839](https://arxiv.org/abs/2412.13839)), arXiv:2412.13839 \[quant-ph]. + diff --git a/docs/addons/qiskit-addon-sqd/install.mdx b/docs/addons/qiskit-addon-sqd/install.mdx new file mode 100644 index 00000000000..fc531c3f920 --- /dev/null +++ b/docs/addons/qiskit-addon-sqd/install.mdx @@ -0,0 +1,63 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Sample-based quantum diagonalization (SQD)" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is ensure your Python environment is set up correctly. To create a new environment: + +## Pre-Installation + +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 +``` + +There are two primary ways to install this package – from PyPI or source. The preferred method is to install from PyPI: + +## Install from PyPI + +```sh +pip install 'qiskit-addon-sqd' +``` + +## Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-addon-sqd` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-sqd.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-sqd +``` + +The next step is to install `qiskit-addon-sqd` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-addon-utils/_toc.json b/docs/addons/qiskit-addon-utils/_toc.json new file mode 100644 index 00000000000..cb3ae27d2ad --- /dev/null +++ b/docs/addons/qiskit-addon-utils/_toc.json @@ -0,0 +1,49 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Qiskit addon utilities 0.3", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-addon-utils" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-addon-utils/install" + }, + { + "title": "How-To Guides", + "children": [ + { + "title": "Use edge coloring to reduce the depth of quantum circuits", + "url": "/docs/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth" + }, + { + "title": "Slicing circuits using qiskit_addon_utils.slicing", + "url": "/docs/addons/qiskit-addon-utils/how_tos/create-circuit-slices" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/qiskit-addon-utils" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Qiskit addon utilities API reference", + "url": "/docs/api/qiskit-addon-utils" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth.ipynb b/docs/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth.ipynb new file mode 100644 index 00000000000..bb3fbddcd6c --- /dev/null +++ b/docs/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth.ipynb @@ -0,0 +1,299 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Use edge coloring to reduce the depth of quantum circuits\"\n", + "description: \"Use edge coloring to reduce the depth of quantum circuits for the latest version of Qiskit addon utilities\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "4de02100-ba7b-4ac3-8e92-f94844073337", + "metadata": {}, + "source": [ + "# Use edge coloring to reduce the depth of quantum circuits\n", + "\n", + "This guide will show how to use the [qiskit_addon_utils.coloring](/docs/api/qiskit-addon-utils/coloring) module to color edges of a coupling map and use the coloring to more efficiently place gates in a quantum circuit, leading to shallower circuits.\n", + "\n", + "The point of edge-coloring a graph is to find a set of edge colors such that no two edges of the same color share a common node. For a quantum processor, this means that gates along like-colored edges (qubit connections) may be run simultaneously and that the circuit will execute faster. We can use this to define our circuit in a way that is more amenable to the hardware it should run on." + ] + }, + { + "cell_type": "markdown", + "id": "4b16cf04-941e-4b02-932e-f5e6301b9a5b", + "metadata": {}, + "source": [ + "First, we will specify a backend and visualize its qubit topology." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9da8d173-8ce9-47a9-a24f-ab32e45f8337", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from qiskit_ibm_runtime.fake_provider import FakeSherbrooke\n", + "\n", + "backend = FakeSherbrooke()\n", + "coupling_map = backend.coupling_map" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9b986eac-93c6-453a-bfe1-2546681ab38b", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "skip-execution" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from rustworkx.visualization import graphviz_draw\n", + "\n", + "graphviz_draw(coupling_map.graph, method=\"neato\")" + ] + }, + { + "cell_type": "markdown", + "id": "eb4e5115-f2a9-4c62-8c5d-96f6bb6c8762", + "metadata": {}, + "source": [ + "Next we use the [qiskit_addon_utils.coloring.auto_color_edges](/docs/api/qiskit-addon-utils/coloring#auto_color_edges) function to generate an edge coloring. This is a light wrapper for [rustworkx.graph_greedy_edge_color](https://www.rustworkx.org/dev/apiref/rustworkx.graph_greedy_edge_color.html) that does some minor post-processing of the output. Namely, this function removes an arbitrary edge direction if an edge is bi-directional and combines the edge and color information into a dictionary format." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6700931f-cf94-4ad6-8012-d73c0bfd7efa", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 144 edges and 3 unique colors.\n", + "The edges are specified by length-2 tuples: (e.g. (1, 0)). \n", + "The \"colors\" are actually specified by a set of integers: {0, 1, 2}\n" + ] + } + ], + "source": [ + "from qiskit_addon_utils.coloring import auto_color_edges\n", + "from rustworkx import PyDiGraph\n", + "\n", + "coloring = auto_color_edges(coupling_map)\n", + "\n", + "# Create a colored graph for visualization\n", + "eagle_r3 = PyDiGraph()\n", + "eagle_r3.extend_from_weighted_edge_list(\n", + " [(source, target, color) for ((source, target), color) in coloring.items()]\n", + ")\n", + "print(f\"There are {len(coloring)} edges and {len(set(coloring.values()))} unique colors.\")\n", + "print(\n", + " f'The edges are specified by length-2 tuples: (e.g. {next(iter(coloring))}).\\\n", + " \\nThe \"colors\" are actually specified by a set of integers: {set(coloring.values())}'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c3f482a1-6868-4cda-b8ed-8a8489794707", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "skip-execution" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def color_edge_3color(edge):\n", + " \"\"\"Return the color of an edge.\"\"\"\n", + " # Map integers of a 3-coloring to color names\n", + " color_dict = {0: \"red\", 1: \"green\", 2: \"blue\"}\n", + " return {\"color\": color_dict[edge]}\n", + "\n", + "\n", + "graphviz_draw(eagle_r3, edge_attr_fn=color_edge_3color, method=\"neato\")" + ] + }, + { + "cell_type": "markdown", + "id": "90d56de5-d43e-4509-8c68-f42ec0f198bb", + "metadata": {}, + "source": [ + "Next, let's make a circuit with a ``CZGate`` on each edge and observe its depth. For this first circuit, we will just naively place a gate on each edge. We will place barriers in ``depth-1`` increments (using [qiskit_addon_utils.slicing.slice_by_depth](/docs/api/qiskit-addon-utils/slicing#slice_by_depth)) to illustrate how crucial edge-coloring is when designing a hardware-efficient quantum circuit." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2806ec31-05e8-4a4c-832b-888150506191", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The circuit has depth: 37\n" + ] + }, + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit import QuantumCircuit\n", + "from qiskit_addon_utils.slicing import combine_slices, slice_by_depth\n", + "\n", + "circuit = QuantumCircuit(backend.num_qubits)\n", + "\n", + "for edge in coupling_map.graph.edge_list():\n", + " circuit.cz(edge[0], edge[1])\n", + "\n", + "print(f\"The circuit has depth: {circuit.depth()}\")\n", + "\n", + "# Add barriers in depth-1 increments\n", + "slices = slice_by_depth(circuit, max_slice_depth=1)\n", + "circuit = combine_slices(slices, include_barriers=True)\n", + "\n", + "circuit.draw(\"mpl\", fold=-1)" + ] + }, + { + "cell_type": "markdown", + "id": "15cf56a0-954e-482a-8ff8-4645b7d18444", + "metadata": {}, + "source": [ + "Now let's place all of the gates of a given color at the same time. We can see the depth is reduced from ``37`` to ``3``. This is because we can take advantage of the fact that all gates on a given color can run at the same time." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "03a5f389-94c0-4496-9fe6-262dca62a0e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The circuit has depth: 3\n" + ] + }, + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from collections import defaultdict\n", + "\n", + "circuit = QuantumCircuit(backend.num_qubits)\n", + "\n", + "# Make a reverse coloring dict\n", + "color_to_edge = defaultdict(list)\n", + "for edge, color in coloring.items():\n", + " color_to_edge[color].append(edge)\n", + "\n", + "# Place edges in order of color\n", + "for edges in color_to_edge.values():\n", + " for edge in edges:\n", + " circuit.cz(edge[0], edge[1])\n", + "\n", + "print(f\"The circuit has depth: {circuit.depth(lambda x: x.name != 'barrier')}\")\n", + "\n", + "# Add barriers in depth-1 increments\n", + "slices = slice_by_depth(circuit, max_slice_depth=1)\n", + "circuit = combine_slices(slices, include_barriers=True)\n", + "\n", + "circuit.draw(\"mpl\", fold=-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" + }, + "toc": { + "base_numbering": 0 + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/addons/qiskit-addon-utils/how_tos/create-circuit-slices.ipynb b/docs/addons/qiskit-addon-utils/how_tos/create-circuit-slices.ipynb new file mode 100644 index 00000000000..03ebc9cbedf --- /dev/null +++ b/docs/addons/qiskit-addon-utils/how_tos/create-circuit-slices.ipynb @@ -0,0 +1,358 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "frontmatter", + "metadata": {}, + "source": [ + "---\n", + "title: \"Slicing circuits using qiskit_addon_utils.slicing\"\n", + "description: \"Slicing circuits using qiskit_addon_utils.slicing for the latest version of Qiskit addon utilities\"\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "4de02100-ba7b-4ac3-8e92-f94844073337", + "metadata": {}, + "source": [ + "# Slicing circuits using ``qiskit_addon_utils.slicing``\n", + "\n", + "Qiskit loosely describes [layers](/docs/api/qiskit/qiskit.dagcircuit.DAGCircuit) as being depth-1 partitions of the circuit across all qubits. Some Qiskit addons make use of the term **slices** to describe layers of arbitrary depth. More concretely, slices can be defined as time-like partitions of a [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) which span all qubits. Similar to layers, composing all slices of a `QuantumCircuit` produces a circuit which is semantically equivalent to the original." + ] + }, + { + "cell_type": "markdown", + "id": "9f419feb-5d35-489e-909c-c8e7ea2caaec", + "metadata": {}, + "source": [ + "The [qiskit_addon_utils.slicing](/docs/api/qiskit-addon-utils/slicing#module-qiskit_addon_utils.slicing) module provides a few utilities for partitioning ``QuantumCircuit``s into slices. This is for example useful for [operator backpropagation](https://github.com/qiskit/qiskit-addon-obp/) We will give an overview of those tools in this guide.\n", + "\n", + "**Note:** Throughout this guide, we will slice the circuit and use [qiskit_addon_utils.slicing.combine_slices](/docs/api/qiskit-addon-utils/slicing#combine_slices) to recombine the slices with barriers to make it easier to visualize the slices." + ] + }, + { + "cell_type": "markdown", + "id": "c6c74021-216c-43fd-b37a-91817e3d3978", + "metadata": {}, + "source": [ + "First, we'll create a circuit from which we'll create slices." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "9da8d173-8ce9-47a9-a24f-ab32e45f8337", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "from qiskit import QuantumCircuit\n", + "\n", + "num_qubits = 9\n", + "qc = QuantumCircuit(num_qubits)\n", + "qc.ry(np.pi / 4, range(num_qubits))\n", + "qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]\n", + "qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]\n", + "qc.cx(qubits_1[:-1], qubits_2)\n", + "qc.cx(qubits_2, qubits_1[1:])\n", + "qc.cx(qubits_1[-1], qubits_1[0])\n", + "qc.rx(np.pi / 4, range(num_qubits))\n", + "qc.rz(np.pi / 4, range(num_qubits))\n", + "qc.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "968d1e85-ab12-4383-865f-f0199308a9f2", + "metadata": {}, + "source": [ + "In the case where there is no clear way to exploit the structure of the circuit for back-propagation, a user may wish to simply partition their circuit into slices of a given depth. Here, we'll separate this circuit into depth-1 slices." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f883fa2e-f943-4c49-af45-fb26cadb72b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit_addon_utils.slicing import combine_slices, slice_by_depth\n", + "\n", + "slices = slice_by_depth(qc, 1)\n", + "combined_slices = combine_slices(slices, include_barriers=True)\n", + "combined_slices.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "4debef73-ba54-4eb1-887c-df6d022c9082", + "metadata": {}, + "source": [ + "Now let's try depth-2." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "bc5ebecb-6c8b-4f51-8199-b236a0ba3328", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slices = slice_by_depth(qc, 2)\n", + "combined_slices = combine_slices(slices, include_barriers=True)\n", + "combined_slices.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "482bc11c-47a3-45ba-9633-98bcca5b44a9", + "metadata": {}, + "source": [ + "In many cases, such as Trotter circuits, it may be advantageous to slice by gate type. Slices holding a given gate type will be further split out into depth-1 slices, as there is little downside in doing so." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1a5d5aff-f988-4f14-ad25-9f0105202b77", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit_addon_utils.slicing import slice_by_gate_types\n", + "\n", + "slices = slice_by_gate_types(qc)\n", + "combined_slices = combine_slices(slices, include_barriers=True)\n", + "combined_slices.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "5e389e7e-1d29-4e9c-b44e-a5182da5cb5b", + "metadata": {}, + "source": [ + "If your circuit was designed to exploit the physical qubit connectivity, you may want to create slices based on an edge coloring. Here, we will assign a 3-coloring to the circuit edges and slice the circuit with respect to the edge coloring. This only affects non-local gates. Single qubit gates will be added to their own slices by gate type." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "110b5844-972e-46d1-bfc5-20a30b69f814", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit_addon_utils.slicing import slice_by_coloring\n", + "\n", + "# Assign a color to each set of connected qubits\n", + "coloring = {}\n", + "for i in range(num_qubits - 1):\n", + " coloring[(i, i + 1)] = i % 3\n", + "coloring[(num_qubits - 1, 0)] = 2\n", + "\n", + "# Create a circuit with operations added in order of color\n", + "qc = QuantumCircuit(num_qubits)\n", + "qc.ry(np.pi / 4, range(num_qubits))\n", + "edges = [edge for color in range(3) for edge in coloring if coloring[edge] == color]\n", + "for edge in edges:\n", + " qc.cx(edge[0], edge[1])\n", + "qc.rx(np.pi / 4, range(num_qubits))\n", + "qc.rz(np.pi / 4, range(num_qubits))\n", + "\n", + "# Create slices by edge color\n", + "slices = slice_by_coloring(qc, coloring=coloring)\n", + "combined_slices = combine_slices(slices, include_barriers=True)\n", + "combined_slices.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "d05b0cad-bbed-48cd-8520-3e2904cde058", + "metadata": {}, + "source": [ + "For more custom slicing strategies a user may wish to place barriers in the locations they want to slice and use the [slice_by_barriers](/docs/api/qiskit-addon-utils/slicing#slice_by_barriers) function. Here, we will create 3 slices, one for each rotation layer and one for the entangling layer." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "948aa715-22e8-41e1-943d-de7eb0aa379c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = QuantumCircuit(num_qubits)\n", + "qc.ry(np.pi / 4, range(num_qubits))\n", + "qc.barrier()\n", + "qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]\n", + "qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]\n", + "qc.cx(qubits_1[:-1], qubits_2)\n", + "qc.cx(qubits_2, qubits_1[1:])\n", + "qc.cx(qubits_1[-1], qubits_1[0])\n", + "qc.barrier()\n", + "qc.rx(np.pi / 4, range(num_qubits))\n", + "qc.rz(np.pi / 4, range(num_qubits))\n", + "qc.draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "markdown", + "id": "6511c580-bcc6-462c-9d17-2435adae311a", + "metadata": {}, + "source": [ + "We will not draw the re-combined slices as a single circuit since it would look identical to the input circuit. Instead, below we draw each slice on its own." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9f766d69-e10c-4aa8-a3bc-2057dab7a69a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit_addon_utils.slicing import slice_by_barriers\n", + "\n", + "slices = slice_by_barriers(qc)\n", + "slices[0].draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a00c8743-f3b9-460c-ba2c-ffc634e99e0e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slices[1].draw(\"mpl\", scale=0.6)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "791bd07b-739a-45ec-a3e9-2d068f8bd960", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slices[2].draw(\"mpl\", scale=0.6)" + ] + } + ], + "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-utils/how_tos/index.mdx b/docs/addons/qiskit-addon-utils/how_tos/index.mdx new file mode 100644 index 00000000000..43d1f96fd27 --- /dev/null +++ b/docs/addons/qiskit-addon-utils/how_tos/index.mdx @@ -0,0 +1,12 @@ +--- +title: "How-To Guides" +description: "How-To Guides for the latest version of Qiskit addon utilities" +--- + +# How-To Guides + +This page summarizes the available how-to guides. + +* [Use edge coloring to reduce the depth of quantum circuits](color-device-edges-to-improve-depth) +* [Slicing circuits using `qiskit_addon_utils.slicing`](create-circuit-slices) + diff --git a/docs/addons/qiskit-addon-utils/index.mdx b/docs/addons/qiskit-addon-utils/index.mdx new file mode 100644 index 00000000000..6c011bb6337 --- /dev/null +++ b/docs/addons/qiskit-addon-utils/index.mdx @@ -0,0 +1,45 @@ +--- +title: "Qiskit addon utilities" +description: "Documentation for the latest version of Qiskit addon utilities" +--- + +# Qiskit addon utilities + +[Qiskit addons](/docs/guides/addons) are a collection of modular tools for building utility-scale workloads powered by Qiskit. + +This package contains functionality which is meant to supplement workflows involving one or more Qiskit addons. For example, this package contains functions for creating Hamiltonians, generating Trotter time evolution circuits, and slicing and combining quantum circuits in time-wise partitions. + +## Documentation + +All documentation is available [here](https://qiskit.github.io/qiskit-addon-utils/). + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install 'qiskit-addon-utils' +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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-utils/release-notes). + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-addon-utils). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-addon-utils/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-utils/issues/new/choose) for tracking requests and bugs. + +## Citation + +If you use this package in your research, please cite it according to the [CITATION.bib](https://github.com/Qiskit/qiskit-addon-utils/blob/main/CITATION.bib) file. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-addon-utils/blob/main/LICENSE.txt) + diff --git a/docs/addons/qiskit-addon-utils/install.mdx b/docs/addons/qiskit-addon-utils/install.mdx new file mode 100644 index 00000000000..33e239378d2 --- /dev/null +++ b/docs/addons/qiskit-addon-utils/install.mdx @@ -0,0 +1,77 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Qiskit addon utilities" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is 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) + +## Pre-Installation + +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-utils` package is via `PyPI`. + +```sh +pip install 'qiskit-addon-utils' +``` + + + +## Option 2: Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-addon-utils` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-addon-utils.git +``` + +Next, upgrade pip and enter the repository. + +```sh +pip install --upgrade pip +cd qiskit-addon-utils +``` + +The next step is to install `qiskit-addon-utils` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab +``` + diff --git a/docs/addons/qiskit-paulice/_toc.json b/docs/addons/qiskit-paulice/_toc.json new file mode 100644 index 00000000000..c37eb690712 --- /dev/null +++ b/docs/addons/qiskit-paulice/_toc.json @@ -0,0 +1,36 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Qiskit Paulice 0.1", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Documentation Home", + "url": "/docs/addons/qiskit-paulice" + }, + { + "title": "Installation Instructions", + "url": "/docs/addons/qiskit-paulice/install" + }, + { + "title": "GitHub", + "url": "https://github.com/qiskit/qiskit-paulice" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Qiskit Paulice API reference", + "url": "/docs/api/qiskit-paulice" + } + ] + } + ] +} diff --git a/docs/addons/qiskit-paulice/index.mdx b/docs/addons/qiskit-paulice/index.mdx new file mode 100644 index 00000000000..1b44c4d6c12 --- /dev/null +++ b/docs/addons/qiskit-paulice/index.mdx @@ -0,0 +1,89 @@ +--- +title: "Qiskit addon: Paulice" +description: "Documentation for the latest version of Qiskit Paulice" +--- + +# Qiskit addon: Paulice + +## Overview + +`qiskit-paulice` is a package for embedding hardware efficient Pauli checks into arbitrary Clifford circuits on arbitrary qubit connectivities using spacetime stabilizer codes. These checks can be used to detect logical errors during circuit execution. Postselecting only samples with no detected errors can improve the fidelity of states sampled with a quantum processor at the cost of some ancilla qubits and an increase in sampling overhead. This method is particularly suited to near-term hardware since it has a much milder overhead in qubits and gates compared to fault tolerant quantum computing, while having a better sampling overhead than error mitigation methods such as ZNE or PEC [\[1\]](#id3). + +Although spacetime Pauli checks may be used to implement standalone error detection routines, they are also relevant in the context of error mitigation and error correction. Error detection can complement error mitigation techniques such as probabilistic error cancellation (PEC) by capturing some of the noise affecting gates and measurements, reducing the impact of the noise channel being inverted and thus reducing the sampling overhead. They may also be viewed as an early step toward practical fault tolerance since implementing stabilizer codes to protect data qubits from logical errors is a core concept of traditional error correction. Since this method provides single shot access to the quantum state it can be used in both sampling-based and expectation value-based workflows. + +### Finding good sets of spacetime Pauli checks + +[![Quantum circuit cartoon](/docs/images/addons/qiskit-paulice/paulice.avif)](_images/paulice-png) + +A set of spacetime Pauli checks is “good” if each check is valid, low weight, and effective. + +A check is comprised of a number of controlled Pauli rotations, $P$, placed on some wires in the circuit, $w$: $\{(P_1,w_1), ..., (P_k,w_k)\}$. For a given check, the controls lie on a single ancilla qubit, and the rotations occur on wires of a single target qubit. For Clifford circuits, a check is valid if its backpropagated product is a stabilizer of the state prepared by the ideal circuit: $\prod_{i}B(P_i,w_i) \in S$, where $B(P,w)$ is the backpropagator of $P$ from $w$ to the beginning of the circuit, and $S$ is the set of all stabilizers of the circuit. + +A check is low weight if it requires few entangling gates to implement. The check picking algorithm will favor checks that are low weight and provide the most effective error detection. + +A check is effective if it captures much more error than it introduces. A Pauli check is comprised of a number of entangling gates and thus introduces some additional gate noise into the calculation. It is important to ensure each additional check brings some additional error detection capability. The effectiveness of a set of checks can be approximated by composing the Pauli errors that are uncovered by the checks into a postselected noise channel and calculating its impact. Minimizing the sampling overhead for implementing the uncovered inverse noise channel is a solid heuristic for selecting good checks, as it gives an indication of how much error the checks can’t detect. A slower but more realistic approach is to perform a Monte Carlo sampling from the noisy state and empirically compute the logical error rate of the postselected distribution. Both of these approaches are available as built-in cost functions in the `qiskit_paulice.add_pauli_checks` function. + +### Postselecting samples based on syndrome data + +In this package a check is implemented using entangling gates between one ancilla qubit and one target qubit. Each ancilla starts in $|0\rangle$, so $Z_\text{anc}$ stabilizes its input state. Forward propagating $Z_\text{anc}$ from the beginning of the ancilla through the entire checked circuit yields a Pauli operator on the output which may be higher weight and extend into the payload circuit. The qubit indices on which this output operator has non-identity terms are called the check’s support, and the check passes if the bits, $b$, in the check’s support have even parity: $\bigoplus_{i=1} b_i = 0$. A sample is kept if each check produces $0$ for its parity check. + +## Code example + +Check out the [tutorial](tutorials/index) for an overview of how to use this package to improve the fidelity of a stabilizer state prepared from noisy QPU samples. + +## Features + +* Automatic noise model creation from backend benchmark data +* Rust accelerated check finding +* 3 built-in algorithms for check finding +* Evaluate efficacy of checks based on sampling overhead of postselected inverse noise channel or logical error rate based on Monte Carlo sampling of noisy state +* Helper functionality for finding ancilla/target qubit pairs for a given backend + +## Known issues + +* Idling noise is not provided via `NoiseModel.get_backend` and is ignored during check picking +* While many stochastic steps in the algorithm are controllable with a random seed, some features have randomness not controllable with a seed. Specifically, the following kwargs values for `add_pauli_checks` will cause undeterministic check picking: `cost="LER"`, `method="genetic"`, and `method="windowed_genetic"`. For deterministic behavior use `add_pauli_checks(..., cost="gamma", method="windowed")`, which are the default values. + +## Future work + +* Support for handling non-Clifford systems +* More support for analyzing postselected noise channel +* Handling idling noise when picking checks +* Controllable randomness for logical error rate cost function and genetic search algorithms + +## Installation + +We encourage installing this package via `pip`, when possible: + +```bash +pip install qiskit-paulice +``` + +For more installation information refer to the [installation instructions](install) in the documentation. + +## Deprecation Policy + +We follow [semantic versioning](https://semver.org/) and are 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. + +## Contributing + +The source code is available [on GitHub](https://github.com/Qiskit/qiskit-paulice). + +The developer guide is located at [CONTRIBUTING.md](https://github.com/Qiskit/qiskit-paulice/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-paulice/issues/new/choose) for tracking requests and bugs. + +## License + +[Apache License 2.0](https://github.com/Qiskit/qiskit-paulice/blob/main/LICENSE.txt) + + + +## References + + + +\[[1](#id1)] + +Simon Martiel, Ali Javadi-Abhari, [Low-overhead error detection with spacetime codes](https://arxiv.org/abs/2504.15725), arXiv:2504.15725 \[quant-ph]. + diff --git a/docs/addons/qiskit-paulice/install.mdx b/docs/addons/qiskit-paulice/install.mdx new file mode 100644 index 00000000000..18e727515d2 --- /dev/null +++ b/docs/addons/qiskit-paulice/install.mdx @@ -0,0 +1,64 @@ +--- +title: "Installation Instructions" +description: "Installation Instructions for the latest version of Qiskit Paulice" +--- + +# Installation Instructions + +Let’s see how to install the package. The first thing to do is ensure your Python environment is set up correctly. To create a new environment: + +## Pre-Installation + +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 +``` + +There are two primary ways to install this package – from PyPI or source. The preferred method is to install from PyPI: + +## Install from PyPI + +```sh +pip install qiskit-paulice +``` + +## Install from Source + +Users who wish to develop in the repository or run the notebooks locally may want to install from source. + +If so, the first step is to clone the `qiskit-paulice` repository. + +```sh +git clone git@github.com:Qiskit/qiskit-paulice.git +``` + +Next, install the Rust toolchain, upgrade pip, and enter the repository. Refer to the [Rust documentation](https://www.rust-lang.org/tools/install) for instructions on installing the toolchain. + +```sh +### ### +pip install --upgrade pip +cd pauli-prop +``` + +The next step is to install `qiskit-paulice` to the virtual environment. If you plan on running the notebooks, install the notebook dependencies in order to run all the visualizations in the notebooks. If you plan on developing in the repository, you may 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. + +```python +cd docs/ +jupyter lab' +``` + diff --git a/docs/api/pauli-prop/_package.json b/docs/api/pauli-prop/_package.json new file mode 100644 index 00000000000..0418261f855 --- /dev/null +++ b/docs/api/pauli-prop/_package.json @@ -0,0 +1,4 @@ +{ + "name": "pauli-prop", + "version": "0.2.0" +} diff --git a/docs/api/pauli-prop/_toc.json b/docs/api/pauli-prop/_toc.json new file mode 100644 index 00000000000..90a7b0c1cda --- /dev/null +++ b/docs/api/pauli-prop/_toc.json @@ -0,0 +1,18 @@ +{ + "title": "Pauli propagation", + "children": [ + { + "title": "API index", + "url": "/docs/api/pauli-prop" + }, + { + "title": "pauli_prop.propagation", + "url": "/docs/api/pauli-prop/propagation", + "untranslatable": true + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/pauli-prop", + "parentLabel": "Pauli propagation" +} diff --git a/docs/api/pauli-prop/index.mdx b/docs/api/pauli-prop/index.mdx new file mode 100644 index 00000000000..7281977d8fe --- /dev/null +++ b/docs/api/pauli-prop/index.mdx @@ -0,0 +1,11 @@ +--- +title: Pauli propagation API documentation (latest version) +description: Index of all the modules in the latest version of pauli-prop. +--- + + + +# `pauli-prop` API reference + +* [Propagation (`pauli_prop.propagation`)](propagation) + diff --git a/docs/api/pauli-prop/propagation.mdx b/docs/api/pauli-prop/propagation.mdx new file mode 100644 index 00000000000..276eae90a1d --- /dev/null +++ b/docs/api/pauli-prop/propagation.mdx @@ -0,0 +1,287 @@ +--- +title: propagation (latest version) +description: API reference for pauli_prop.propagation in the latest version of pauli-prop +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: pauli_prop.propagation +--- + + + + + +# Propagation + +`pauli_prop.propagation` + +Functions for performing Pauli propagation. + +### RotationGates + + + Bases: [`NamedTuple`](https://docs.python.org/3/library/typing.html#typing.NamedTuple) + + An intermediate minimal representation of a `QuantumCircuit`. + + Supported Pauli rotations: rx/rxx, ry/ryy, rz/rzz, PauliEvolutionGate + + Create new instance of RotationGates(gates, qargs, thetas) + + **Parameters** + + * **gates** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...],* [*dtype*](https://numpy.org/doc/stable/reference/generated/numpy.dtype.html#numpy.dtype)*\[*[*bool*](https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.bool)*]]]*) + * **qargs** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]]*) + * **thetas** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) + + #### append\_circuit\_instruction + + + Parses a circuit instruction and appends its data to the internal lists. + + **Parameters** + + * **inst** ([*CircuitInstruction*](/docs/api/qiskit/qiskit.circuit.CircuitInstruction)) – The circuit instruction to parse and append + * **qargs** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – The list of qubit indices of the instruction in the context of its circuit + * **num\_qubits** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of qubits of the circuit containing this instruction + * **clifford** ([*Clifford*](/docs/api/qiskit/qiskit.quantum_info.Clifford) *| None*) – An optional Clifford through which the provided instruction should be moved. The Clifford must act on all qubits in the circuit. + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Unsupported gate encountered in circuit + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – If given, `clifford` must act on all qubits in circuit + + **Return type** + + None + + + #### count + + + Return number of occurrences of value. + + + #### gates + + + A ZX-calculus-like representation of the gates. + + + #### index + + + Return first index of value. + + Raises ValueError if the value is not present. + + + #### qargs + + + The qubit indices acted upon by each gate. + + + #### thetas + + + The rotation angles of all gates. + + + +### circuit\_to\_rotation\_gates + + + Converts the provided circuit to an intermediate representation. + + Supports Pauli rotation gates (‘rx/rxx’, ‘ry/ryy’, ‘rz/rzz’, ‘PauliEvolutionGate’) and Pauli-Lindblad error channels, specified as [PauliLindbladError](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.PauliLindbladError.html#qiskit_aer.noise.PauliLindbladError) instructions. + + **Parameters** + + **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – + + The circuit to convert. May contain Pauli rotations (rx/rxx, ry/ryy, rz/rzz, PauliEvolutionGate) and optionally [PauliLindbladError](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.PauliLindbladError.html#qiskit_aer.noise.PauliLindbladError) instructions. + + **Returns** + + A [`RotationGates`](#pauli_prop.propagation.RotationGates "pauli_prop.propagation.RotationGates") instance if the circuit does not contain `PauliLindbladError` instructions; otherwise, a `NoisyRotationGates` instance is returned. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – when an unsupported gate is encountered in `circuit`. + + **Return type** + + [*RotationGates*](#pauli_prop.propagation.RotationGates "pauli_prop.propagation.RotationGates") | *NoisyRotationGates* + + +### propagate\_through\_rotation\_gates + + + Propagate a sparse Pauli operator, $O$, through a circuit (represented in `rot_gates`), $U$. + + For Schrödinger propagation: $U O U^{\dagger}$. For Heisenberg propagation: $U^{\dagger} O U$. + + If `rot_gates` is a `NoisyRotationGates` instance, the operator will be propagated through each noise generator. The coefficient associated with each term in `operator`, $c_i$, will be damped according to each anti-commuting error generator’s rate, $r_i$: $c_i *= exp(-2.0 * r_i)$. + + In general, the memory and time required for propagating through a circuit grows exponentially with the number of operations in the circuit due to the exponential growth in the number of terms of the operator in the Pauli basis. To regulate this exponential difficulty, one may truncate small Pauli terms (i.e. set them to zero), resulting in a bias proportional to the magnitudes of the truncated terms. After propagating through each operation in the circuit, terms are truncated with respect to two parameters: + + * Only the `max_terms` largest Pauli components are kept; any smaller terms will be truncated. This option makes it possible to estimate in advance how much time and memory will suffice for the computation. + * Terms with magnitudes less than `atol` are truncated (set to zero). + + + This function pre-allocates space in memory for the full-sized operator and operator buffer. It is the caller’s responsibility to ensure they have enough memory to hold operators containing `max_terms` terms. When `max_terms` is `None`, the memory and time requirements typically grow exponentially with the number of operations in the circuit. + + + **Parameters** + + * **operator** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – The operator to propagate + * **rot\_gates** ([*RotationGates*](#pauli_prop.propagation.RotationGates "pauli_prop.propagation.RotationGates") *| NoisyRotationGates*) – A circuit represented in the form of [`RotationGates`](#pauli_prop.propagation.RotationGates "pauli_prop.propagation.RotationGates"). + * **max\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum number of terms the operator may contain as it is propagated + * **atol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – Terms with coeff magnitudes less than this will not be added to the operator as it is propagated. This parameter is not a guarantee on the accuracy of the returned operator. + * **frame** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – `s` for Schrödinger evolution `h` for Heisenberg evolution + + **Returns** + + The evolved operator and one-norm of all truncated coefficients. + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `frame` is neither `h` nor `s`. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `atol` is negative. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `max_terms` is not positive. + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp), [float](https://docs.python.org/3/library/functions.html#float)] + + +### propagate\_through\_circuit + + + Propagate a sparse Pauli operator, $O$, through a circuit, $U$. + + Supports Pauli rotation gates (‘rx/rxx’, ‘ry/ryy’, ‘rz/rzz’, ‘PauliEvolutionGate’) and Pauli-Lindblad error channels, specified as [PauliLindbladError](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.PauliLindbladError.html#qiskit_aer.noise.PauliLindbladError) instructions. + + For Schrödinger propagation: $U O U^{\dagger}$. For Heisenberg propagation: $U^{\dagger} O U$. + + If `circuit` contains Pauli-Lindblad noise instructions, the operator will be propagated through each noise generator. The coefficient associated with each term in `operator`, $c_i$, will be damped according to each anti-commuting error generator’s rate, $r_i$: $c_i *= exp(-2.0 * r_i)$. + + In general, the memory and time required for propagating through a circuit grows exponentially with the number of operations in the circuit due to the exponential growth in the number of terms of the operator in the Pauli basis. To regulate this exponential difficulty, one may truncate small Pauli terms (i.e. set them to zero), resulting in a bias proportional to the magnitudes of the truncated terms. After propagating through each operation in the circuit, terms are truncated with respect to two parameters: + + * Only the `max_terms` largest Pauli components are kept; any smaller terms will be truncated. This option makes it possible to estimate in advance how much time and memory will suffice for the computation. + * Terms with magnitudes less than `atol` are truncated (set to zero). + + + This function pre-allocates space in memory for the full-sized operator and operator buffer. It is the caller’s responsibility to ensure they have enough memory to hold operators containing `max_terms` terms. When `max_terms` is `None`, the memory and time requirements typically grow exponentially with the number of operations in the circuit. + + + **Parameters** + + * **operator** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – The operator to propagate + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – The circuit through which the operator will be propagated + * **max\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum number of terms the operator may contain as it is propagated + * **atol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – Terms with coeff magnitudes less than this will not be added to the operator as it is propagated. This parameter is not a guarantee on the accuracy of the returned operator. + * **frame** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – `s` for Schrödinger evolution `h` for Heisenberg evolution + + **Returns** + + The evolved operator + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `frame` is neither `h` nor `s`. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `atol` is negative. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `max_terms` is not positive. + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp), [float](https://docs.python.org/3/library/functions.html#float)] + + +### propagate\_through\_operator + + + Propagate an operator, op1 or $O$, through another operator, op2 or $U$. + + For Schrödinger evolution: $U O U^{\dagger}$. + + For Heisenberg evolution: $U^{\dagger} O U$. + + Evolution is performed in the Pauli basis by summing terms of the form $U_i O_j U_k$ (neglecting the dagger, see note below). The number of such terms is cubic in operator size (len(op1) \* len(op2)\*\*2) and will generally include many duplicate Paulis. + + Setting max\_terms produces an approximate result, where only the max\_terms largest terms (in coefficient magnitude) are computed. This can be much faster but results in some error due to truncation of smaller terms. + + The approximate computation involves two parts: searching for the terms to keep, then computing those terms. Increasing `search_step` greatly (cubically) speeds up the search, at an accuracy cost that is often small. + + It is possible that some Paulis present in the kept terms would have also appeared in the truncated terms. Because such truncated terms are never computed, they cannot possibly be merged into the kept terms sharing the same Pauli. Thus, the `n``th-largest term in the approximate result is not guaranteed to equal the nth-largest term in the exact result. Likewise, convergence to the exact result with increasing ``max_terms` can be non-monotonic. + + + $O$ is assumed to be Hermitian ($O_j$ = $O_j^{\dagger}$) + + + **Parameters** + + * **op1** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – The operator to propagate + + * **op2** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – The operator through which to propagate + + * **max\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – + + When not `None`, produces an approximate result including only the `max_terms` largest terms in the direct product of the three operators in Pauli space. + + When `max_terms` is `None` and the number of qubits is \< 12, the propagation will be performed in the computational basis using matrix multiplication. For systems > 12 qubits, all Pauli terms are computed and summed; however, this is usually not a good way to compute exact evolution due to the many duplicate terms present. + + * **coerce\_op1\_traceless** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – A flag denoting whether to remove identity terms from the output operator. + + * **num\_leading\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of terms in `op1` to conjugate by every term in `op2`. The set of included terms is expanded to include its union with the set of terms $U_i O_j U_i^{\dagger}$, for $j < num_leading_terms$. This can improve accuracy for the leading components of O in the output, at some computational runtime cost. + + * **frame** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – s for Schrödinger evolution h for Heisenberg evolution + + * **atol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – Terms in the evolved operator with magnitudes below `atol` will be truncated + + * **search\_step** ([*int*](https://docs.python.org/3/library/functions.html#int)) – A parameter that can speed up the search of the very large 3D space to identify the `max_terms` largest terms in the product. Setting this step size >1 accelerates that search by a factor of `search_step**3`, at a potential cost in accuracy. This inaccuracy is expected to be small for `search_step**3 << max_terms`. + + **Returns** + + The transformed operator + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `frame` is neither `s` nor `h`. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `search_step` is not positive. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `max_terms` contains an invalid value. + + **Return type** + + [*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) + + +### evolve\_through\_cliffords + + + Evolve (Schrödinger frame) all non-Clifford instructions through all Clifford gates in the circuit. + + This shifts all recognized Clifford gates to the beginning of the circuit and updates the bases of Pauli-rotation gates (e.g. `RxGate`, `RzzGate`, `PauliEvolutionGate`) and [PauliLindbladError](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.noise.PauliLindbladError.html#qiskit_aer.noise.PauliLindbladError) channels. Other operations are not supported. See [Pauli.evolve docs](/docs/en/api/qiskit/qiskit.quantum_info.Pauli#evolve) for more info about evolution of Paulis by Cliffords. + + The effect is similar to going to the Clifford interaction picture in [arXiv:2306.04797](https://arxiv.org/abs/2306.04797) but without mapping all rotation angle magnitudes to be $\leq \pi/4$. + + The function returns two objects representing the Clifford and non-Clifford parts of the circuit. + + **Parameters** + + **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – The `QuantumCircuit` to transform. Can contain only Pauli-rotation gates, `PauliLindbladError` (appended to the circuit as quantum channels), and recognized Clifford gates. + + **Returns** + + * **Clifford** - A single all-qubit [Clifford](/docs/en/api/qiskit/qiskit.quantum_info.Clifford) representing the first part of the circuit + * **QuantumCircuit** - A circuit containing the remaining, transformed part of the circuit + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Input circuit contains unsupported gate + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[*Clifford*](/docs/api/qiskit/qiskit.quantum_info.Clifford), [*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)] + + diff --git a/docs/api/qiskit-addon-aqc-tensor/_toc.json b/docs/api/qiskit-addon-aqc-tensor/_toc.json index 99cd2329015..e07759d3615 100644 --- a/docs/api/qiskit-addon-aqc-tensor/_toc.json +++ b/docs/api/qiskit-addon-aqc-tensor/_toc.json @@ -102,5 +102,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-aqc-tensor", + "parentLabel": "Approximate quantum compilation (AQC-Tensor)" } diff --git a/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-ansatz-block.mdx b/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-ansatz-block.mdx index 7d8fbbe874e..56b2f68d6be 100644 --- a/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-ansatz-block.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-ansatz-block.mdx @@ -13,7 +13,7 @@ python_api_name: qiskit_addon_aqc_tensor.ansatz_generation.AnsatzBlock Ansatz block. - This is the base class of all blocks returned by [`generate_ansatz_from_circuit()`](ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"). + This is the base class of all blocks returned by [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"). Initialize the ansatz block. diff --git a/docs/api/qiskit-addon-aqc-tensor/ansatz-generation.mdx b/docs/api/qiskit-addon-aqc-tensor/ansatz-generation.mdx index d52d674a09e..17cac80ed2f 100644 --- a/docs/api/qiskit-addon-aqc-tensor/ansatz-generation.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/ansatz-generation.mdx @@ -16,13 +16,13 @@ python_api_name: qiskit_addon_aqc_tensor.ansatz_generation Tools for generating ansatz circuits. -| | | -| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -| [`AnsatzBlock`](ansatz-generation-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.AnsatzBlock") | Ansatz block. | -| [`OneQubitAnsatzBlock`](ansatz-generation-one-qubit-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.OneQubitAnsatzBlock") | One-qubit ansatz block. | -| [`TwoQubitAnsatzBlock`](ansatz-generation-two-qubit-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.TwoQubitAnsatzBlock") | Two-qubit ansatz block. | -| [`ZXZ`](ansatz-generation-zxz "qiskit_addon_aqc_tensor.ansatz_generation.ZXZ") | One-qubit ansatz block based on the ZXZ decomposition. | -| [`KAK`](ansatz-generation-kak "qiskit_addon_aqc_tensor.ansatz_generation.KAK") | Two-qubit ansatz block based on the KAK decomposition. | +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| [`AnsatzBlock`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.AnsatzBlock") | Ansatz block. | +| [`OneQubitAnsatzBlock`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-one-qubit-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.OneQubitAnsatzBlock") | One-qubit ansatz block. | +| [`TwoQubitAnsatzBlock`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-two-qubit-ansatz-block "qiskit_addon_aqc_tensor.ansatz_generation.TwoQubitAnsatzBlock") | Two-qubit ansatz block. | +| [`ZXZ`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-zxz "qiskit_addon_aqc_tensor.ansatz_generation.ZXZ") | One-qubit ansatz block based on the ZXZ decomposition. | +| [`KAK`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation-kak "qiskit_addon_aqc_tensor.ansatz_generation.KAK") | Two-qubit ansatz block based on the KAK decomposition. | ### generate\_ansatz\_from\_circuit diff --git a/docs/api/qiskit-addon-aqc-tensor/release-notes.mdx b/docs/api/qiskit-addon-aqc-tensor/release-notes.mdx index 6afd69c5e71..d9e1899c6c5 100644 --- a/docs/api/qiskit-addon-aqc-tensor/release-notes.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/release-notes.mdx @@ -34,7 +34,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* This adds a workaround for an issue sometimes encountered when calling [`TwoQubitWeylDecomposition`](/docs/api/qiskit/qiskit.synthesis.TwoQubitWeylDecomposition) inside [`generate_ansatz_from_circuit()`](ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"). Sometimes an error is thrown despite the input matrix being unitary to numerical precision, and returning ``is_unitary()`()`` as `True`. This is an ongoing issue in qiskit, see [https://github.com/Qiskit/qiskit/issues/4159](https://github.com/Qiskit/qiskit/issues/4159) for more details. +* This adds a workaround for an issue sometimes encountered when calling [`TwoQubitWeylDecomposition`](/docs/api/qiskit/qiskit.synthesis.TwoQubitWeylDecomposition) inside [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"). Sometimes an error is thrown despite the input matrix being unitary to numerical precision, and returning ``is_unitary()`()`` as `True`. This is an ongoing issue in qiskit, see [https://github.com/Qiskit/qiskit/issues/4159](https://github.com/Qiskit/qiskit/issues/4159) for more details. With this workaround, any time the error is encountered, it will retry by transpiling the two-qubit circuit (from which the matrix is derived) prior to passing the matrix to [`TwoQubitWeylDecomposition`](/docs/api/qiskit/qiskit.synthesis.TwoQubitWeylDecomposition). It should be noted that this is does not fix the root issue, and is not guaranteed to always work, but has been observed to work for some instances. @@ -48,7 +48,7 @@ in_page_toc_max_heading_level: 2 ### Prelude -This release renames the primary objective to [`MaximizeStateFidelity`](objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity") and adds support for Qiskit 2.0. +This release renames the primary objective to [`MaximizeStateFidelity`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity") and adds support for Qiskit 2.0. @@ -56,7 +56,7 @@ This release renames the primary objective to [`MaximizeStateFidelity`](objectiv ### New Features -* Adds the [`parametrize_circuit()`](ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.parametrize_circuit "qiskit_addon_aqc_tensor.ansatz_generation.parametrize_circuit") function for generating a parametrized version of a given circuit. In contrast to [`generate_ansatz_from_circuit()`](ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"), `parametrize_circuit` does not change the types of gates in the circuit. It simply replaces numerical parameters with free parameters. +* Adds the [`parametrize_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.parametrize_circuit "qiskit_addon_aqc_tensor.ansatz_generation.parametrize_circuit") function for generating a parametrized version of a given circuit. In contrast to [`generate_ansatz_from_circuit()`](/docs/api/qiskit-addon-aqc-tensor/ansatz-generation#qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit "qiskit_addon_aqc_tensor.ansatz_generation.generate_ansatz_from_circuit"), `parametrize_circuit` does not change the types of gates in the circuit. It simply replaces numerical parameters with free parameters. * Support for Python 3.13. @@ -66,7 +66,7 @@ This release renames the primary objective to [`MaximizeStateFidelity`](objectiv ### Upgrade Notes -* The `OneMinusFidelity` objective function has been renamed and is now known as [`MaximizeStateFidelity`](objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity"). As a related change, one should now call the [`loss_function()`](objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function") method to obtain the value and gradient of the loss function, instead of calling the instance directly (through its `__call__` method). +* The `OneMinusFidelity` objective function has been renamed and is now known as [`MaximizeStateFidelity`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity"). As a related change, one should now call the [`loss_function()`](/docs/api/qiskit-addon-aqc-tensor/objective#qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function "qiskit_addon_aqc_tensor.objective.MaximizeStateFidelity.loss_function") method to obtain the value and gradient of the loss function, instead of calling the instance directly (through its `__call__` method). * Qiskit SDK version 1.3 or higher is now required. Qiskit SDK version 2.0 is now supported. diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps.mdx index 7938c162095..b3e9f4b7a21 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps.mdx @@ -9,7 +9,7 @@ python_api_name: qiskit_addon_aqc_tensor.simulation.aer.QiskitAerMPS # QiskitAerMPS - Bases: [`TensorNetworkState`](simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkState "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkState") + Bases: [`TensorNetworkState`](/docs/api/qiskit-addon-aqc-tensor/simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkState "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkState") Qiskit Aer representation of a matrix-product state. diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings.mdx index 7f61ff29ba5..0b82ab1ee23 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings.mdx @@ -9,7 +9,7 @@ python_api_name: qiskit_addon_aqc_tensor.simulation.aer.QiskitAerSimulationSetti # QiskitAerSimulationSettings - Bases: [`TensorNetworkSimulationSettings`](simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkSimulationSettings "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkSimulationSettings") + Bases: [`TensorNetworkSimulationSettings`](/docs/api/qiskit-addon-aqc-tensor/simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkSimulationSettings "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkSimulationSettings") Qiskit Aer simulator settings. diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation-aer.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation-aer.mdx index 89cee6e3bfb..4bd85900fb2 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation-aer.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation-aer.mdx @@ -16,10 +16,10 @@ python_api_name: qiskit_addon_aqc_tensor.simulation.aer Qiskit Aer MPS simulator as a tensor network backend. -| | | -| --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| [`QiskitAerMPS`](simulation-aer-qiskit-aer-mps "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerMPS") | Qiskit Aer representation of a matrix-product state. | -| [`QiskitAerSimulationSettings`](simulation-aer-qiskit-aer-simulation-settings "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerSimulationSettings") | Qiskit Aer simulator settings. | +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| [`QiskitAerMPS`](/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerMPS") | Qiskit Aer representation of a matrix-product state. | +| [`QiskitAerSimulationSettings`](/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerSimulationSettings") | Qiskit Aer simulator settings. | ## Functions diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator.mdx index 0f0ed87192f..1785b72f811 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator.mdx @@ -9,7 +9,7 @@ python_api_name: qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator # QuimbSimulator - Bases: [`TensorNetworkSimulationSettings`](simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkSimulationSettings "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkSimulationSettings") + Bases: [`TensorNetworkSimulationSettings`](/docs/api/qiskit-addon-aqc-tensor/simulation#qiskit_addon_aqc_tensor.simulation.TensorNetworkSimulationSettings "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkSimulationSettings") Settings for Quimb simulator. diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation-quimb.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation-quimb.mdx index 4cc33251025..636c84bce2c 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation-quimb.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation-quimb.mdx @@ -16,11 +16,11 @@ python_api_name: qiskit_addon_aqc_tensor.simulation.quimb Quimb as a tensor network backend. -| | | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| [`QuimbCircuitFactory`](simulation-quimb-quimb-circuit-factory "qiskit_addon_aqc_tensor.simulation.quimb.QuimbCircuitFactory") | Quimb circuit factory. | -| [`QuimbSimulator`](simulation-quimb-quimb-simulator "qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator") | Settings for Quimb simulator. | -| [`QiskitQuimbConversionContext`](simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext") | Contains information about Qiskit-to-Quimb conversion, necessary to recover Qiskit parameters. | +| | | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`QuimbCircuitFactory`](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-circuit-factory "qiskit_addon_aqc_tensor.simulation.quimb.QuimbCircuitFactory") | Quimb circuit factory. | +| [`QuimbSimulator`](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-quimb-simulator "qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator") | Settings for Quimb simulator. | +| [`QiskitQuimbConversionContext`](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext") | Contains information about Qiskit-to-Quimb conversion, necessary to recover Qiskit parameters. | ## Functions @@ -41,7 +41,7 @@ Quimb as a tensor network backend. **Return type** - [`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`Circuit`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/circuit/index.html#quimb.tensor.circuit.Circuit), [`QiskitQuimbConversionContext`](simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext")] + [`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`Circuit`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/circuit/index.html#quimb.tensor.circuit.Circuit), [`QiskitQuimbConversionContext`](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext")] **Parameters** @@ -61,7 +61,7 @@ Quimb as a tensor network backend. **Parameters** * **circ\_opt** ([*Circuit*](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/circuit/index.html#quimb.tensor.circuit.Circuit)) - * **ctx** ([*QiskitQuimbConversionContext*](simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext")) + * **ctx** ([*QiskitQuimbConversionContext*](/docs/api/qiskit-addon-aqc-tensor/simulation-quimb-qiskit-quimb-conversion-context "qiskit_addon_aqc_tensor.simulation.quimb.QiskitQuimbConversionContext")) ### tnoptimizer\_objective\_kwargs diff --git a/docs/api/qiskit-addon-aqc-tensor/simulation.mdx b/docs/api/qiskit-addon-aqc-tensor/simulation.mdx index 95fc0d9bb65..db100e0ebaf 100644 --- a/docs/api/qiskit-addon-aqc-tensor/simulation.mdx +++ b/docs/api/qiskit-addon-aqc-tensor/simulation.mdx @@ -34,7 +34,7 @@ In each function below, the documentation shows every distinct implementation av Create a tensor-network state by simulating a quantum circuit. - The type of tensor-network state will correspond to the type of the `settings` object. For instance, a [`QiskitAerSimulationSettings`](simulation-aer-qiskit-aer-simulation-settings "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerSimulationSettings") will result in this function returning a [`QiskitAerMPS`](simulation-aer-qiskit-aer-mps "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerMPS"). + The type of tensor-network state will correspond to the type of the `settings` object. For instance, a [`QiskitAerSimulationSettings`](/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-simulation-settings "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerSimulationSettings") will result in this function returning a [`QiskitAerMPS`](/docs/api/qiskit-addon-aqc-tensor/simulation-aer-qiskit-aer-mps "qiskit_addon_aqc_tensor.simulation.aer.QiskitAerMPS"). **Parameters** diff --git a/docs/api/qiskit-addon-cutting/_toc.json b/docs/api/qiskit-addon-cutting/_toc.json index 1c70a374aeb..6f31ae77e65 100644 --- a/docs/api/qiskit-addon-cutting/_toc.json +++ b/docs/api/qiskit-addon-cutting/_toc.json @@ -117,5 +117,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-cutting", + "parentLabel": "Circuit cutting" } diff --git a/docs/api/qiskit-addon-cutting/instructions.mdx b/docs/api/qiskit-addon-cutting/instructions.mdx index 897592b2eec..82e82155a9e 100644 --- a/docs/api/qiskit-addon-cutting/instructions.mdx +++ b/docs/api/qiskit-addon-cutting/instructions.mdx @@ -16,8 +16,8 @@ python_api_name: qiskit_addon_cutting.instructions Quantum circuit `Instruction`s useful for circuit cutting. -| | | -| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | -| [`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") | An instruction for denoting a wire cut location. | -| [`Move`](instructions-move "qiskit_addon_cutting.instructions.Move") | A two-qubit instruction representing a reset of the second qubit followed by a swap. | +| | | +| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") | An instruction for denoting a wire cut location. | +| [`Move`](/docs/api/qiskit-addon-cutting/instructions-move "qiskit_addon_cutting.instructions.Move") | A two-qubit instruction representing a reset of the second qubit followed by a swap. | diff --git a/docs/api/qiskit-addon-cutting/qiskit-addon-cutting.mdx b/docs/api/qiskit-addon-cutting/qiskit-addon-cutting.mdx index 9b7aed2c1b6..f6009dd840b 100644 --- a/docs/api/qiskit-addon-cutting/qiskit-addon-cutting.mdx +++ b/docs/api/qiskit-addon-cutting/qiskit-addon-cutting.mdx @@ -19,19 +19,19 @@ Circuit cutting. ### cut\_wires - Transform all [`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions in a circuit to [`Move`](instructions-move "qiskit_addon_cutting.instructions.Move") instructions marked for cutting. + Transform all [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions in a circuit to [`Move`](/docs/api/qiskit-addon-cutting/instructions-move "qiskit_addon_cutting.instructions.Move") instructions marked for cutting. - The returned circuit will have one newly allocated qubit for every [`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instruction. + The returned circuit will have one newly allocated qubit for every [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instruction. - See Sec. 3 and Appendix A of [2302.03366v1](https://arxiv.org/abs/2302.03366v1) for more information about the two different representations of wire cuts: single-qubit ([`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire")) vs. two-qubit ([`Move`](instructions-move "qiskit_addon_cutting.instructions.Move")). + See Sec. 3 and Appendix A of [2302.03366v1](https://arxiv.org/abs/2302.03366v1) for more information about the two different representations of wire cuts: single-qubit ([`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire")) vs. two-qubit ([`Move`](/docs/api/qiskit-addon-cutting/instructions-move "qiskit_addon_cutting.instructions.Move")). **Parameters** - **circuit** ([`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – Original circuit with [`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions + **circuit** ([`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – Original circuit with [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions **Returns** - New circuit with [`CutWire`](instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions replaced by [`Move`](instructions-move "qiskit_addon_cutting.instructions.Move") instructions wrapped in `TwoQubitQPDGate`s + New circuit with [`CutWire`](/docs/api/qiskit-addon-cutting/instructions-cut-wire "qiskit_addon_cutting.instructions.CutWire") instructions replaced by [`Move`](/docs/api/qiskit-addon-cutting/instructions-move "qiskit_addon_cutting.instructions.Move") instructions wrapped in `TwoQubitQPDGate`s **Return type** @@ -74,14 +74,14 @@ Circuit cutting. ### partition\_circuit\_qubits - Replace all nonlocal gates belonging to more than one partition with instances of [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate"). + Replace all nonlocal gates belonging to more than one partition with instances of [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate"). - [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s belonging to a single partition will not be affected. + [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s belonging to a single partition will not be affected. **Parameters** * **circuit** ([`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – The circuit to partition - * **partition\_labels** ([`Sequence`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable)]) – A sequence containing a partition label for each qubit in the input circuit. Nonlocal gates belonging to more than one partition will be replaced with [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s. + * **partition\_labels** ([`Sequence`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable)]) – A sequence containing a partition label for each qubit in the input circuit. Nonlocal gates belonging to more than one partition will be replaced with [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s. * **inplace** ([`bool`](https://docs.python.org/3/library/functions.html#bool)) – Flag denoting whether to copy the input circuit before acting on it **Return type** @@ -90,7 +90,7 @@ Circuit cutting. **Returns** - The output circuit with each nonlocal gate spanning two partitions replaced by a [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") + The output circuit with each nonlocal gate spanning two partitions replaced by a [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") **Raises** @@ -105,9 +105,9 @@ Circuit cutting. If `partition_labels` is provided, then qubits with matching partition labels will be grouped together, and non-local gates spanning more than one partition will be cut apart. The label `None` is treated specially: any qubit with that partition label must be unused in the circuit. - If `partition_labels` is not provided, then it will be determined automatically from the connectivity of the circuit. This automatic determination ignores any [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s in the `circuit`, as these denote instructions that are explicitly destined for cutting. The resulting partition labels, in the automatic case, will be consecutive integers starting with 0. Qubits which are idle throughout the circuit will be assigned a partition label of `None`. + If `partition_labels` is not provided, then it will be determined automatically from the connectivity of the circuit. This automatic determination ignores any [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s in the `circuit`, as these denote instructions that are explicitly destined for cutting. The resulting partition labels, in the automatic case, will be consecutive integers starting with 0. Qubits which are idle throughout the circuit will be assigned a partition label of `None`. - All cut instructions will be replaced with [`SingleQubitQPDGate`](qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate")s. + All cut instructions will be replaced with [`SingleQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate")s. If provided, `observables` will be separated along the boundaries specified by the partition labels. @@ -137,7 +137,7 @@ Circuit cutting. ### cut\_gates - Transform specified gates into [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s. + Transform specified gates into [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s. **Parameters** @@ -147,11 +147,11 @@ Circuit cutting. **Return type** - [`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit), [`list`](https://docs.python.org/3/library/stdtypes.html#list)\[[`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis")]] + [`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit), [`list`](https://docs.python.org/3/library/stdtypes.html#list)\[[`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis")]] **Returns** - A copy of the input circuit with the specified gates replaced with [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s and a list of [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") instances – one for each decomposed gate. + A copy of the input circuit with the specified gates replaced with [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s and a list of [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") instances – one for each decomposed gate. **Raises** @@ -244,7 +244,7 @@ $$ **Parameters** * **subcircuits** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[Hashable, QuantumCircuit]*) - * **bases** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*QPDBasis*](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis")*]*) + * **bases** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*QPDBasis*](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis")*]*) * **subobservables** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[Hashable, PauliList] | None*) #### bases @@ -299,7 +299,7 @@ $$ **Returns** - A circuit containing [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances. The subcircuits resulting from cutting these gates will be runnable on the devices meeting the `constraints`. + A circuit containing [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances. The subcircuits resulting from cutting these gates will be runnable on the devices meeting the `constraints`. **A metadata dictionary:** diff --git a/docs/api/qiskit-addon-cutting/qpd.mdx b/docs/api/qiskit-addon-cutting/qpd.mdx index 68b5a096c5b..858b99ac96f 100644 --- a/docs/api/qiskit-addon-cutting/qpd.mdx +++ b/docs/api/qiskit-addon-cutting/qpd.mdx @@ -16,12 +16,12 @@ python_api_name: qiskit_addon_cutting.qpd Main quasiprobability decomposition functionality. -| | | -| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") | Basis in which to decompose an operation. | -| [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") | Base class for a gate to be decomposed using quasiprobability decomposition. | -| [`SingleQubitQPDGate`](qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate") | Single qubit gate to be decomposed using quasiprobability decomposition. | -| [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") | Two qubit gate to be decomposed using quasiprobability decomposition. | +| | | +| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | +| [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") | Basis in which to decompose an operation. | +| [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") | Base class for a gate to be decomposed using quasiprobability decomposition. | +| [`SingleQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate") | Single qubit gate to be decomposed using quasiprobability decomposition. | +| [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") | Two qubit gate to be decomposed using quasiprobability decomposition. | ### WeightType @@ -52,7 +52,7 @@ Main quasiprobability decomposition functionality. **Parameters** - * **qpd\_bases** ([`Sequence`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis")]) – The [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") objects from which to generate weights + * **qpd\_bases** ([`Sequence`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)\[[`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis")]) – The [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") objects from which to generate weights * **num\_samples** ([`float`](https://docs.python.org/3/library/functions.html#float)) – Controls the number of weights to generate **Return type** @@ -61,7 +61,7 @@ Main quasiprobability decomposition functionality. **Returns** - A mapping from a given decomposition to its weight. Keys are tuples of indices – one index per input [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis"). The indices correspond to a specific decomposition mapping in the basis. + A mapping from a given decomposition to its weight. Keys are tuples of indices – one index per input [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis"). The indices correspond to a specific decomposition mapping in the basis. Values are tuples. The first element is a number corresponding to the weight of the contribution. The second element is the [`WeightType`](#qiskit_addon_cutting.qpd.WeightType "qiskit_addon_cutting.qpd.WeightType"), either `EXACT` or `SAMPLED`. @@ -84,15 +84,15 @@ Main quasiprobability decomposition functionality. **Returns** - Circuit which has had all its [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances decomposed into local operations. + Circuit which has had all its [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances decomposed into local operations. The circuit will contain a new, final classical register to contain the QPD measurement outcomes (accessible at `retval.cregs[-1]`). **Raises** - * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – An index in `instruction_ids` corresponds to a gate which is not a [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instance. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – An index in `instruction_ids` corresponds to a gate which is not a [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instance. * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – A list within instruction\_ids is not length 1 or 2. - * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – The total number of indices in `instruction_ids` does not equal the number of [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances in the circuit. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – The total number of indices in `instruction_ids` does not equal the number of [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances in the circuit. * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Gates within the same decomposition hold different QPD bases. * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Length of `map_ids` does not equal the number of decompositions in the circuit. @@ -100,19 +100,19 @@ Main quasiprobability decomposition functionality. ### qpdbasis\_from\_instruction - Generate a [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") object, given a supported operation. + Generate a [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") object, given a supported operation. All two-qubit gates which implement the [`to_matrix()`](/docs/api/qiskit/qiskit.circuit.Gate#to_matrix) method are supported. This should include the vast majority of gates with no unbound parameters, but there are some special cases (see, e.g., [qiskit issue #10396](https://github.com/Qiskit/qiskit/issues/10396)). - The [`Move`](instructions-move "qiskit_addon_cutting.instructions.Move") operation, which can be used to specify a wire cut, is also supported. + The [`Move`](/docs/api/qiskit-addon-cutting/instructions-move "qiskit_addon_cutting.instructions.Move") operation, which can be used to specify a wire cut, is also supported. **Return type** - [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis") + [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.qpd_basis.QPDBasis") **Returns** - The newly-instantiated [`QPDBasis`](qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") object + The newly-instantiated [`QPDBasis`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis "qiskit_addon_cutting.qpd.QPDBasis") object **Raises** diff --git a/docs/api/qiskit-addon-cutting/release-notes.mdx b/docs/api/qiskit-addon-cutting/release-notes.mdx index e2f503bca53..0572c561fdd 100644 --- a/docs/api/qiskit-addon-cutting/release-notes.mdx +++ b/docs/api/qiskit-addon-cutting/release-notes.mdx @@ -60,9 +60,9 @@ The most notable change in this release is that the package has been renamed to ### New Features -* A new `minimum_reached` field has been added to the metadata outputted by `circuit_knitting.cutting.find_cuts()` to check if the cut-finder found a cut scheme that minimized the sampling overhead. Note that the search algorithm employed by the cut-finder is *guaranteed* to find the optimal solution, that is, the solution with the minimum sampling overhead, provided it is allowed to run long enough. The user is free to time-restrict the search by passing in suitable values for `max_backjumps` and/or `max_gamma` to [`OptimizationParameters`](qiskit-addon-cutting#optimizationparameters "qiskit_addon_cutting.OptimizationParameters"). If the search is terminated prematurely in this way, the metadata may indicate that the minimum was not reached, even though the returned solution was actually the optimal solution. This would mean that the search that was performed was not exhaustive enough to prove that the returned solution was optimal. +* A new `minimum_reached` field has been added to the metadata outputted by `circuit_knitting.cutting.find_cuts()` to check if the cut-finder found a cut scheme that minimized the sampling overhead. Note that the search algorithm employed by the cut-finder is *guaranteed* to find the optimal solution, that is, the solution with the minimum sampling overhead, provided it is allowed to run long enough. The user is free to time-restrict the search by passing in suitable values for `max_backjumps` and/or `max_gamma` to [`OptimizationParameters`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#optimizationparameters "qiskit_addon_cutting.OptimizationParameters"). If the search is terminated prematurely in this way, the metadata may indicate that the minimum was not reached, even though the returned solution was actually the optimal solution. This would mean that the search that was performed was not exhaustive enough to prove that the returned solution was optimal. -* When specifying instances of [`OptimizationParameters`](qiskit-addon-cutting#optimizationparameters "qiskit_addon_cutting.OptimizationParameters") that are inputted to `circuit_knitting.cutting.find_cuts()`, the user can now control whether the cut-finder looks only for gate cuts, only for wire cuts, or both, by setting the bools `gate_lo` and `wire_lo` appropriately. The default value of both of these is set to `True` and so the default search considers the possibility of both gate and wire cuts. +* When specifying instances of [`OptimizationParameters`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#optimizationparameters "qiskit_addon_cutting.OptimizationParameters") that are inputted to `circuit_knitting.cutting.find_cuts()`, the user can now control whether the cut-finder looks only for gate cuts, only for wire cuts, or both, by setting the bools `gate_lo` and `wire_lo` appropriately. The default value of both of these is set to `True` and so the default search considers the possibility of both gate and wire cuts. @@ -76,7 +76,7 @@ The most notable change in this release is that the package has been renamed to * The `CutQC` subpackage has been removed, along with its two associated utility modules, `circuit_knitting.utils.metrics` and `circuit_knitting.utils.conversion`. Users are now encouraged to use the automatic cut-finding and gate/wire cutting from the `circuit_knitting.cutting` package. -* The behavior of [`separate_circuit()`](utils-transforms#separate_circuit "qiskit_addon_cutting.utils.transforms.separate_circuit") and [`partition_problem()`](qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") have changed so that idle qubits are discarded by default. Previously, each idle qubit was placed in its own subcircuit if `partition_labels` was not provided. +* The behavior of [`separate_circuit()`](/docs/api/qiskit-addon-cutting/utils-transforms#separate_circuit "qiskit_addon_cutting.utils.transforms.separate_circuit") and [`partition_problem()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") have changed so that idle qubits are discarded by default. Previously, each idle qubit was placed in its own subcircuit if `partition_labels` was not provided. @@ -104,7 +104,7 @@ The 0.7.1 release provides a workaround to ensure that the experiments generated ### Other Notes -* The [`generate_cutting_experiments()`](qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") function has been optimized for faster execution. +* The [`generate_cutting_experiments()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") function has been optimized for faster execution. @@ -170,19 +170,19 @@ The 0.7 release introduces an automated cut finding code for the new circuit cut * The minimum supported version of `qiskit` is now 0.45.0, and the minimum supported version of `qiskit-ibm-runtime` is now 0.12.2. CKT also now explicitly requires a version of `qiskit` less than 1.0, as there is no guarantee that the current version of CKT will work with Qiskit 1.0. -* Removed the `circuit_knitting.cutting.qpd.QPDBasis.from_gate` method, which has been deprecated since the 0.3 release. [`QPDBasis.from_instruction()`](qpd-qpd-basis#from_instruction "qiskit_addon_cutting.qpd.QPDBasis.from_instruction") should be used instead. +* Removed the `circuit_knitting.cutting.qpd.QPDBasis.from_gate` method, which has been deprecated since the 0.3 release. [`QPDBasis.from_instruction()`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis#from_instruction "qiskit_addon_cutting.qpd.QPDBasis.from_instruction") should be used instead. * Removed the `circuit_knitting_toolbox` import path. Users should now import from `circuit_knitting` instead. -* Removed the `circuit_knitting.cutting.decompose_gates` function, which has been deprecated since the 0.3 release. [`cut_gates()`](qiskit-addon-cutting#cut_gates "qiskit_addon_cutting.cut_gates") should be used instead. +* Removed the `circuit_knitting.cutting.decompose_gates` function, which has been deprecated since the 0.3 release. [`cut_gates()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#cut_gates "qiskit_addon_cutting.cut_gates") should be used instead. * Removed the `circuit_knitting.cutting.cutting_evaluation` module, which has been deprecated since the 0.4 release. Users should first call `circuit_knitting.cutting.generate_cutting_experiments()` to generate the subexperiment circuits and then submit these circuits directly to the desired Sampler(s). -* Removed the `circuit_knitting.cutting.qpd.generate_qpd_samples` function, which has been deprecated since the 0.3 release. [`generate_qpd_weights()`](qpd#generate_qpd_weights "qiskit_addon_cutting.qpd.generate_qpd_weights") should be used instead. +* Removed the `circuit_knitting.cutting.qpd.generate_qpd_samples` function, which has been deprecated since the 0.3 release. [`generate_qpd_weights()`](/docs/api/qiskit-addon-cutting/qpd#generate_qpd_weights "qiskit_addon_cutting.qpd.generate_qpd_weights") should be used instead. -* Removed the `circuit_knitting.cutting.qpd.qpdbasis_from_gate` function, which has been deprecated since the 0.3 release. [`qpdbasis_from_instruction()`](qpd#qpdbasis_from_instruction "qiskit_addon_cutting.qpd.qpdbasis_from_instruction") should be used instead. +* Removed the `circuit_knitting.cutting.qpd.qpdbasis_from_gate` function, which has been deprecated since the 0.3 release. [`qpdbasis_from_instruction()`](/docs/api/qiskit-addon-cutting/qpd#qpdbasis_from_instruction "qiskit_addon_cutting.qpd.qpdbasis_from_instruction") should be used instead. -* The [`generate_cutting_experiments()`](qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") function now performs some optimizations on the generated circuits before returning them to the user. In particular, it performs the [`RemoveResetInZeroState`](/docs/api/qiskit/qiskit.transpiler.passes.RemoveResetInZeroState), [`RemoveFinalReset`](utils-transpiler-passes-remove-final-reset "qiskit_addon_cutting.utils.transpiler_passes.RemoveFinalReset"), and [`ConsolidateResets`](utils-transpiler-passes-consolidate-resets "qiskit_addon_cutting.utils.transpiler_passes.ConsolidateResets") passes, so that circuits with cut wires and no re-used qubits are transformed into subexperiments that contain no `Reset`s. This allows such circuits to work on a greater variety of hardware backends. +* The [`generate_cutting_experiments()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") function now performs some optimizations on the generated circuits before returning them to the user. In particular, it performs the [`RemoveResetInZeroState`](/docs/api/qiskit/qiskit.transpiler.passes.RemoveResetInZeroState), [`RemoveFinalReset`](/docs/api/qiskit-addon-cutting/utils-transpiler-passes-remove-final-reset "qiskit_addon_cutting.utils.transpiler_passes.RemoveFinalReset"), and [`ConsolidateResets`](/docs/api/qiskit-addon-cutting/utils-transpiler-passes-consolidate-resets "qiskit_addon_cutting.utils.transpiler_passes.ConsolidateResets") passes, so that circuits with cut wires and no re-used qubits are transformed into subexperiments that contain no `Reset`s. This allows such circuits to work on a greater variety of hardware backends. @@ -190,7 +190,7 @@ The 0.7 release introduces an automated cut finding code for the new circuit cut ### Bug Fixes -* It is now possible to serialize [`SingleQubitQPDGate`](qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate")s using [`qpy`](/docs/api/qiskit/qpy#module-qiskit.qpy), but some other issues with serialization and deserialization still remain. See issue [#455](https://github.com/Qiskit/qiskit-addon-cutting/issues/445) for details. +* It is now possible to serialize [`SingleQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-single-qubit-qpd-gate "qiskit_addon_cutting.qpd.SingleQubitQPDGate")s using [`qpy`](/docs/api/qiskit/qpy#module-qiskit.qpy), but some other issues with serialization and deserialization still remain. See issue [#455](https://github.com/Qiskit/qiskit-addon-cutting/issues/445) for details. @@ -246,7 +246,7 @@ The primary purpose of this release is to swap the order of the classical regist ### Prelude -The primary goal of this release is to modify the circuit cutting workflow to enable direct use of the Sampler primitive. Previously, the Sampler was called in `execute_experiments()`, a function which is now deprecated in favor of [`generate_cutting_experiments()`](qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments"). +The primary goal of this release is to modify the circuit cutting workflow to enable direct use of the Sampler primitive. Previously, the Sampler was called in `execute_experiments()`, a function which is now deprecated in favor of [`generate_cutting_experiments()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments"). @@ -254,7 +254,7 @@ The primary goal of this release is to modify the circuit cutting workflow to en ### New Features -* Added a module, `circuit_knitting.cutting.cutting_experiments`, which is intended to hold functions used for generating the quantum experiments needed for circuit cutting. This module will initially hold one function, [`generate_cutting_experiments()`](qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments"), which can be used to generate quantum experiments, given an input circuit containing [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances, some observables, and a number of times the joint quasi-probability distribution for the cuts should be sampled. +* Added a module, `circuit_knitting.cutting.cutting_experiments`, which is intended to hold functions used for generating the quantum experiments needed for circuit cutting. This module will initially hold one function, [`generate_cutting_experiments()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments"), which can be used to generate quantum experiments, given an input circuit containing [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances, some observables, and a number of times the joint quasi-probability distribution for the cuts should be sampled. @@ -264,9 +264,9 @@ The primary goal of this release is to modify the circuit cutting workflow to en * The `circuit-knitting-toolbox` Python package now depends on `qiskit` rather than `qiskit-terra`. This should have no user-visible effects, but it is something to keep in mind if one sees dependency errors when upgrading CKT. -* The `execute_experiments()` function now returns a [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instance for each circuit partition, rather than the 3D list of quasi-distributions returned previously. The quasi-distribution for each subexperiment can be accessed via the `quasi_dists` field of [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult). The number of QPD bits contained in each subexperiment will be included in the `num_qpd_bits` field of the `metadata` dictionary for each experiment result. The output of this function is still valid as input to [`reconstruct_expectation_values()`](qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values"). +* The `execute_experiments()` function now returns a [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instance for each circuit partition, rather than the 3D list of quasi-distributions returned previously. The quasi-distribution for each subexperiment can be accessed via the `quasi_dists` field of [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult). The number of QPD bits contained in each subexperiment will be included in the `num_qpd_bits` field of the `metadata` dictionary for each experiment result. The output of this function is still valid as input to [`reconstruct_expectation_values()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values"). -* [`reconstruct_expectation_values()`](qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") now takes, as its first argument, a [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instance or a dictionary mapping partition labels to [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instances. This new `results` argument replaces the old `quasi_dists` argument. The [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instances are expected to contain the number of QPD bits used in each circuit input to the Sampler. This should be specified in the `num_qpd_bits` field of the experiment result metadata. +* [`reconstruct_expectation_values()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") now takes, as its first argument, a [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instance or a dictionary mapping partition labels to [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instances. This new `results` argument replaces the old `quasi_dists` argument. The [`SamplerResult`](/docs/api/qiskit/qiskit.primitives.SamplerResult) instances are expected to contain the number of QPD bits used in each circuit input to the Sampler. This should be specified in the `num_qpd_bits` field of the experiment result metadata. @@ -274,7 +274,7 @@ The primary goal of this release is to modify the circuit cutting workflow to en ### Deprecation Notes -* The `execute_experiments()` function has been deprecated. Going forward, users should first call [`generate_cutting_experiments()`](qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") to generate the subexperiment circuits and then submit these circuits directly to the desired Sampler(s). The [tutorials](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) have been updated with this new workflow. +* The `execute_experiments()` function has been deprecated. Going forward, users should first call [`generate_cutting_experiments()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#generate_cutting_experiments "qiskit_addon_cutting.generate_cutting_experiments") to generate the subexperiment circuits and then submit these circuits directly to the desired Sampler(s). The [tutorials](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) have been updated with this new workflow. @@ -311,7 +311,7 @@ The 0.3.0 release introduces significant new features while maintaining backward * [`iSwapGate`](/docs/api/qiskit/qiskit.circuit.library.iSwapGate) * [`DCXGate`](/docs/api/qiskit/qiskit.circuit.library.DCXGate) -* [`partition_problem()`](qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") now works even if `partition_labels` is not explicitly provided. In this case, the labels are determined automatically from the connectivity of the input circuit. For the sake of determining connectivity, [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s are ignored, as these instructions are already marked for cutting. To support this workflow, this release also introduces a new method, [`TwoQubitQPDGate.from_instruction()`](qpd-two-qubit-qpd-gate#from_instruction "qiskit_addon_cutting.qpd.TwoQubitQPDGate.from_instruction"), which allows one to create a [`TwoQubitQPDGate`](qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") that wraps a given instruction. +* [`partition_problem()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") now works even if `partition_labels` is not explicitly provided. In this case, the labels are determined automatically from the connectivity of the input circuit. For the sake of determining connectivity, [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate")s are ignored, as these instructions are already marked for cutting. To support this workflow, this release also introduces a new method, [`TwoQubitQPDGate.from_instruction()`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate#from_instruction "qiskit_addon_cutting.qpd.TwoQubitQPDGate.from_instruction"), which allows one to create a [`TwoQubitQPDGate`](/docs/api/qiskit-addon-cutting/qpd-two-qubit-qpd-gate "qiskit_addon_cutting.qpd.TwoQubitQPDGate") that wraps a given instruction. * Dynamic Definition code has been added to the `cutqc` module. See pull request [#285](https://github.com/Qiskit-Extensions/circuit-knitting-toolbox/pull/285) for details. @@ -333,7 +333,7 @@ The 0.3.0 release introduces significant new features while maintaining backward * The dependency on the `qiskit.opflow` module has been removed from entanglement forging. With this change, Qiskit Nature 0.6.0 is now required. However, Qiskit Nature 0.6.0 is incompatible with Quantum Serverless, so users that wish to use entanglement forging with Quantum Serverless must remain on version 0.2 of the Circuit Knitting Toolbox until [issue #108](https://github.com/Qiskit/qiskit-addon-cutting/issues/108) is resolved. -* [`BaseQPDGate`](qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances in subcircuits returned from [`partition_circuit_qubits()`](qiskit-addon-cutting#partition_circuit_qubits "qiskit_addon_cutting.partition_circuit_qubits") and [`partition_problem()`](qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") will now have labels prefixed with “cut”, rather than “qpd”. +* [`BaseQPDGate`](/docs/api/qiskit-addon-cutting/qpd-base-qpd-gate "qiskit_addon_cutting.qpd.BaseQPDGate") instances in subcircuits returned from [`partition_circuit_qubits()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_circuit_qubits "qiskit_addon_cutting.partition_circuit_qubits") and [`partition_problem()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem") will now have labels prefixed with “cut”, rather than “qpd”. @@ -341,11 +341,11 @@ The 0.3.0 release introduces significant new features while maintaining backward ### Deprecation Notes -* `decompose_gates()` is deprecated and will be removed no sooner than v0.4.0. Users should migrate to the identical [`cut_gates()`](qiskit-addon-cutting#cut_gates "qiskit_addon_cutting.cut_gates") function. +* `decompose_gates()` is deprecated and will be removed no sooner than v0.4.0. Users should migrate to the identical [`cut_gates()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#cut_gates "qiskit_addon_cutting.cut_gates") function. * `generate_qpd_samples()` has been renamed to `generate_qpd_weights()`. The original name will be removed no sooner than version 0.4 of the Circuit Knitting Toolbox. -* `QPDBasis.from_gate()` has been renamed to [`QPDBasis.from_instruction()`](qpd-qpd-basis#from_instruction "qiskit_addon_cutting.qpd.QPDBasis.from_instruction"). The original name is deprecated and will be removed no sooner than CKT v0.4.0. +* `QPDBasis.from_gate()` has been renamed to [`QPDBasis.from_instruction()`](/docs/api/qiskit-addon-cutting/qpd-qpd-basis#from_instruction "qiskit_addon_cutting.qpd.QPDBasis.from_instruction"). The original name is deprecated and will be removed no sooner than CKT v0.4.0. * The top-level name for imports has been renamed from `circuit_knitting_toolbox` to `circuit_knitting`. Furthermore, the following renames have occurred one level deeper: @@ -380,7 +380,7 @@ The 0.3.0 release introduces significant new features while maintaining backward The foundation of the `circuit_cutting` package is the `circuit_knitting_toolbox.circuit_cutting.qpd` sub-package. The `qpd` package allows for easy transformation of [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) gates and wires into elements which may be decomposed to a probabilistic set of basis gates. See `QPDBasis` and `BaseQPDGate` classes for more information. -Additionally, 0.2.0 includes a set of functions which allow for easy implementation of gate cutting workflows. These functions are built on top of the `circuit_knitting_toolbox.circuit_cutting.qpd` package. Like all circuit knitting techniques, gate cutting can be described as three consecutive stages: *decomposition* of a problem, *execution* of many subexperiments, and *reconstruction* of a simulated output of the original problem. These steps may be implemented with the `circuit_cutting` package using only a few primary functions, namely, the [`partition_problem()`](qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem"), `decompose_gates()`, `execute_experiments()`, and [`reconstruct_expectation_values()`](qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") functions. Check out the [tutorials](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) for a look at a couple of example circuit cutting workflows. +Additionally, 0.2.0 includes a set of functions which allow for easy implementation of gate cutting workflows. These functions are built on top of the `circuit_knitting_toolbox.circuit_cutting.qpd` package. Like all circuit knitting techniques, gate cutting can be described as three consecutive stages: *decomposition* of a problem, *execution* of many subexperiments, and *reconstruction* of a simulated output of the original problem. These steps may be implemented with the `circuit_cutting` package using only a few primary functions, namely, the [`partition_problem()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem"), `decompose_gates()`, `execute_experiments()`, and [`reconstruct_expectation_values()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") functions. Check out the [tutorials](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) for a look at a couple of example circuit cutting workflows. @@ -390,7 +390,7 @@ Additionally, 0.2.0 includes a set of functions which allow for easy implementat * Addition of a `qpd` package which allows for easy transformation of [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) gates and wires into elements which may be decomposed to a probabilistic set of basis gates. See `QPDBasis` and `BaseQPDGate` classes for more information. -* Addition of `cutting_decomposition`, `cutting_execution`, and `cutting_reconstruction` modules. These modules provide several functions which allow for easy implementation of gate cutting workflows, namely, the [`partition_problem()`](qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem"), `decompose_gates()`, `execute_experiments()`, and [`reconstruct_expectation_values()`](qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") functions. +* Addition of `cutting_decomposition`, `cutting_execution`, and `cutting_reconstruction` modules. These modules provide several functions which allow for easy implementation of gate cutting workflows, namely, the [`partition_problem()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#partition_problem "qiskit_addon_cutting.partition_problem"), `decompose_gates()`, `execute_experiments()`, and [`reconstruct_expectation_values()`](/docs/api/qiskit-addon-cutting/qiskit-addon-cutting#reconstruct_expectation_values "qiskit_addon_cutting.reconstruct_expectation_values") functions. diff --git a/docs/api/qiskit-addon-cutting/utils-transpiler-passes.mdx b/docs/api/qiskit-addon-cutting/utils-transpiler-passes.mdx index e0b1c77eda9..3298e43b0ff 100644 --- a/docs/api/qiskit-addon-cutting/utils-transpiler-passes.mdx +++ b/docs/api/qiskit-addon-cutting/utils-transpiler-passes.mdx @@ -16,8 +16,8 @@ python_api_name: qiskit_addon_cutting.utils.transpiler_passes Transpiler passes useful for circuit knitting. -| | | -| ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| [`RemoveFinalReset`](utils-transpiler-passes-remove-final-reset "qiskit_addon_cutting.utils.transpiler_passes.RemoveFinalReset") | Remove reset when it is the final instruction on a qubit wire. | -| [`ConsolidateResets`](utils-transpiler-passes-consolidate-resets "qiskit_addon_cutting.utils.transpiler_passes.ConsolidateResets") | Consolidate a run duplicate resets in to a single reset. | +| | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| [`RemoveFinalReset`](/docs/api/qiskit-addon-cutting/utils-transpiler-passes-remove-final-reset "qiskit_addon_cutting.utils.transpiler_passes.RemoveFinalReset") | Remove reset when it is the final instruction on a qubit wire. | +| [`ConsolidateResets`](/docs/api/qiskit-addon-cutting/utils-transpiler-passes-consolidate-resets "qiskit_addon_cutting.utils.transpiler_passes.ConsolidateResets") | Consolidate a run duplicate resets in to a single reset. | diff --git a/docs/api/qiskit-addon-mpf/_toc.json b/docs/api/qiskit-addon-mpf/_toc.json index 99c4b93559b..e68ebca7052 100644 --- a/docs/api/qiskit-addon-mpf/_toc.json +++ b/docs/api/qiskit-addon-mpf/_toc.json @@ -143,5 +143,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-mpf", + "parentLabel": "Multi-product formulas (MPF)" } diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver.mdx index a3b6b5776f1..fec67a50749 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver # CircuitEvolver - Bases: [`Evolver`](backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") + Bases: [`Evolver`](/docs/api/qiskit-addon-mpf/backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") A time-evolution engine based on quantum circuits. - This algorithm performs time-evolution by means of successively applying a quantum circuit corresponding to a single Trotter step to its internal state. More specifically, it builds out a tensor network in the [`CircuitState`](backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState"). As required by the [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm, it tracks a left- and right-hand side of the time-evolution for computing the overlap of two circuits. Depending on [`conjugate`](#qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver.conjugate "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver.conjugate"), an instance of this engine will apply the quantum gates of its template circuit to the corresponding side (see [`quimb_circuit`](backends-quimb-circuit#module-qiskit_addon_mpf.backends.quimb_circuit "qiskit_addon_mpf.backends.quimb_circuit") for more details). + This algorithm performs time-evolution by means of successively applying a quantum circuit corresponding to a single Trotter step to its internal state. More specifically, it builds out a tensor network in the [`CircuitState`](backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState"). As required by the [`DynamicMPF`](/docs/api/qiskit-addon-mpf/dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm, it tracks a left- and right-hand side of the time-evolution for computing the overlap of two circuits. Depending on [`conjugate`](#qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver.conjugate "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver.conjugate"), an instance of this engine will apply the quantum gates of its template circuit to the corresponding side (see [`quimb_circuit`](/docs/api/qiskit-addon-mpf/backends-quimb-circuit#module-qiskit_addon_mpf.backends.quimb_circuit "qiskit_addon_mpf.backends.quimb_circuit") for more details). Initialize a [`CircuitEvolver`](#qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver") instance. @@ -40,7 +40,7 @@ python_api_name: qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver ### evolution\_state - The time-evolution state (see also [`DynamicMPF.evolution_state`](dynamic#evolution_state "qiskit_addon_mpf.dynamic.DynamicMPF.evolution_state")). + The time-evolution state (see also [`DynamicMPF.evolution_state`](/docs/api/qiskit-addon-mpf/dynamic#evolution_state "qiskit_addon_mpf.dynamic.DynamicMPF.evolution_state")). ### circuit diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state.mdx index ce5459f0e06..9e4cce1bf72 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.quimb_circuit.CircuitState # CircuitState - Bases: [`State`](backends#state "qiskit_addon_mpf.backends.interface.State") + Bases: [`State`](/docs/api/qiskit-addon-mpf/backends#state "qiskit_addon_mpf.backends.interface.State") An MPO-like representation of a time-evolution state based on quantum circuits. - This time-evolution state can be evolved on its left- and right-hand side as required by the [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm. + This time-evolution state can be evolved on its left- and right-hand side as required by the [`DynamicMPF`](/docs/api/qiskit-addon-mpf/dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm. Initialize a [`CircuitState`](#qiskit_addon_mpf.backends.quimb_circuit.CircuitState "qiskit_addon_mpf.backends.quimb_circuit.CircuitState") instance. diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-circuit.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-circuit.mdx index 547b7eabbd7..007517da413 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-circuit.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-circuit.mdx @@ -24,10 +24,10 @@ A circuit-based time-evolution backend using [`quimb`](https://quimb.readthedocs ``` -| | | -| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| [`CircuitEvolver`](backends-quimb-circuit-circuit-evolver "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver") | A time-evolution engine based on quantum circuits. | -| [`CircuitState`](backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState") | An MPO-like representation of a time-evolution state based on quantum circuits. | +| | | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| [`CircuitEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver") | A time-evolution engine based on quantum circuits. | +| [`CircuitState`](/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState") | An MPO-like representation of a time-evolution state based on quantum circuits. | ## Underlying method @@ -37,14 +37,14 @@ Quimb boasts direct support for the simulation of quantum circuits in the form o This section shows a simple example to get you started with using this backend. The example shows how to create the three factory functions required for the [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse"). -The [`IdentityStateFactory`](dynamic#identitystatefactory "qiskit_addon_mpf.dynamic.IdentityStateFactory") protocol is already fulfilled by the [`CircuitState`](backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState") constructor, rendering the `identity_factory` argument trivial: +The [`IdentityStateFactory`](dynamic#identitystatefactory "qiskit_addon_mpf.dynamic.IdentityStateFactory") protocol is already fulfilled by the [`CircuitState`](/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-state "qiskit_addon_mpf.backends.quimb_circuit.CircuitState") constructor, rendering the `identity_factory` argument trivial: ```python >>> from qiskit_addon_mpf.backends.quimb_circuit import CircuitState >>> identity_factory = CircuitState ``` -The setup of the [`CircuitEvolver`](backends-quimb-circuit-circuit-evolver "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver") is slightly more involved. It requires a **parameterized** [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) object as its input where the [`Parameter`](/docs/api/qiskit/qiskit.circuit.Parameter) should take the place of the Trotter methods time step (`dt`). +The setup of the [`CircuitEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-circuit-circuit-evolver "qiskit_addon_mpf.backends.quimb_circuit.CircuitEvolver") is slightly more involved. It requires a **parameterized** [`QuantumCircuit`](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) object as its input where the [`Parameter`](/docs/api/qiskit/qiskit.circuit.Parameter) should take the place of the Trotter methods time step (`dt`). To show how such a parameterized Trotter circuit template is constructed, we reuse the same Hamiltonian and second-order Suzuki-Trotter formula as in [`quimb_layers`](backends-quimb-layers#module-qiskit_addon_mpf.backends.quimb_layers "qiskit_addon_mpf.backends.quimb_layers"). diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver.mdx index a0bb46176f4..d077a8df1b0 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver.mdx @@ -13,7 +13,7 @@ python_api_name: qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver A special case of the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") based on layer-wise evolution models. - As also explained in [`quimb_layers`](backends-quimb-layers#module-qiskit_addon_mpf.backends.quimb_layers "qiskit_addon_mpf.backends.quimb_layers"), this implementation extracts the alternating even/odd bond updates implemented inside of the original [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) to become the end users responsibility. It does so, by replacing the single Hamiltonian provided to the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") instance with a sequence of [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") instances. Every single instance of these encodes a single **layer** of interactions. These should enforce the alternating updates of even and odd bonds of the underlying tensor network. + As also explained in [`quimb_layers`](/docs/api/qiskit-addon-mpf/backends-quimb-layers#module-qiskit_addon_mpf.backends.quimb_layers "qiskit_addon_mpf.backends.quimb_layers"), this implementation extracts the alternating even/odd bond updates implemented inside of the original [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) to become the end users responsibility. It does so, by replacing the single Hamiltonian provided to the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") instance with a sequence of [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") instances. Every single instance of these encodes a single **layer** of interactions. These should enforce the alternating updates of even and odd bonds of the underlying tensor network. The motivation for this more complicated interface is that is provides a lot more flexbility and enables users to define custom Trotter product formulas rather than being limited to the ones implemented by `quimb` directly. @@ -22,7 +22,7 @@ python_api_name: qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver **Parameters** * **evolution\_state** ([*quimb\_tebd.MPOState*](backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState") *| MatrixProductState*) – forwarded to [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver"). Please refer to its documentation for more details. - * **layers** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*LayerModel*](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel")*]*) – the list of models describing single layers of interactions. See above as well as the explanations provided in [`quimb_layers`](backends-quimb-layers#module-qiskit_addon_mpf.backends.quimb_layers "qiskit_addon_mpf.backends.quimb_layers"). + * **layers** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*LayerModel*](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel")*]*) – the list of models describing single layers of interactions. See above as well as the explanations provided in [`quimb_layers`](/docs/api/qiskit-addon-mpf/backends-quimb-layers#module-qiskit_addon_mpf.backends.quimb_layers "qiskit_addon_mpf.backends.quimb_layers"). * **args** – any further positional arguments will be forwarded to the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") constructor. * **kwargs** – any further keyword arguments will be forwarded to the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") constructor. diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-layers.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-layers.mdx index 60b040d41f8..e41c3905eca 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-layers.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-layers.mdx @@ -24,16 +24,16 @@ A layer-wise time-evolution backend using [`quimb`](https://quimb.readthedocs.io ``` -| | | -| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [`LayerwiseEvolver`](backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") | A special case of the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") based on layer-wise evolution models. | -| [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") | A model for representing a layer of time-evolution interactions. | +| | | +| -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") | A special case of the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") based on layer-wise evolution models. | +| [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") | A model for representing a layer of time-evolution interactions. | ## Underlying method This module provides a time-evolution backend similar to the TEBD-based one provided by the [`quimb_tebd`](backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd") module. The main difference is that this module gives the user full flexibility for defining their product formulas, thereby not limiting them to the options built into the [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#module-quimb) library. -At its core, the algorithm provided by this module is still a TEBD \[1] algorithm. However, rather than enforcing the alternating updates to the even and odd bonds of the time-evolution state (see also [`quimb_tebd.TEBDEvolver.sweep()`](backends-quimb-tebd-tebd-evolver#sweep "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver.sweep")) this implementation outsources the responsibility of updating bonds in alternating fashion to the definition of multiple time-evolution **layers**. +At its core, the algorithm provided by this module is still a TEBD \[1] algorithm. However, rather than enforcing the alternating updates to the even and odd bonds of the time-evolution state (see also [`quimb_tebd.TEBDEvolver.sweep()`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver#sweep "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver.sweep")) this implementation outsources the responsibility of updating bonds in alternating fashion to the definition of multiple time-evolution **layers**. This is best explained with an example. Let us assume, we have some generic Hamiltonian acting on a 1-dimensional chain of sites. @@ -80,19 +80,19 @@ In the circuit above, we can clearly identify its layer-wise structure. We can e It is not possible to instruct Quimb’s TEBD algorithm to simulate the exact structure of the circuit shown above. The reason for that is a limitation in its interface, as it only accepts the full Hamiltonian to be provided which is then time-evolved using pre-defined Trotter formulas. However, in doing so it does not treat the order of the Pauli terms in a Hamiltonian with any significance (like we do here). -If one wants to compute the dynamic MPF coefficients of a time-evolution employing a product formula structure other than the ones implemented in Quimb (like the example above), then one can use the time-evolution algorithm provided by this module. Rather than taking a single monolithic Hamiltonian whose time-evolution is to be modeled, the [`LayerwiseEvolver`](backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") accepts a list of [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") objects, each one describing an individual layer of the product formula. This gives the user full flexibility in defining the Trotter decomposition down to the most granular level. +If one wants to compute the dynamic MPF coefficients of a time-evolution employing a product formula structure other than the ones implemented in Quimb (like the example above), then one can use the time-evolution algorithm provided by this module. Rather than taking a single monolithic Hamiltonian whose time-evolution is to be modeled, the [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") accepts a list of [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") objects, each one describing an individual layer of the product formula. This gives the user full flexibility in defining the Trotter decomposition down to the most granular level. However, caution must be applied to ensure that the property of TEBD to update even and odd bonds in an alternating manner is still guaranteed. Luckily, for quantum circuits consisting of at most two-qubit gates, this property is satisfied by construction. ## Code example -In this section, we build up on the example above and show how to take a custom Trotter formula and use it to construct a [`LayerwiseEvolver`](backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") which can be used to replace the [`quimb_tebd.TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") in the workflow described in [`quimb_tebd`](backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd"). +In this section, we build up on the example above and show how to take a custom Trotter formula and use it to construct a [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") which can be used to replace the [`quimb_tebd.TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") in the workflow described in [`quimb_tebd`](backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd"). The overall workflow of using this module is the same as of the [`quimb_tebd`](backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd") module, so be sure to read those instructions as well. -Simply put, we must convert each one of the circuit `layers` (see above) into a [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") instance. For this purpose, we can use its [`from_quantum_circuit()`](backends-quimb-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.quimb_layers.LayerModel.from_quantum_circuit") method. +Simply put, we must convert each one of the circuit `layers` (see above) into a [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") instance. For this purpose, we can use its [`from_quantum_circuit()`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.quimb_layers.LayerModel.from_quantum_circuit") method. ```python >>> from qiskit_addon_mpf.backends.quimb_layers import LayerModel @@ -102,7 +102,7 @@ Simply put, we must convert each one of the circuit `layers` (see above) into a In the code above you can see how simple the conversion is for layers which contain only two-qubit gates acting on mutually exclusive qubits (which layers of depth 1 guarantee). -However, we must be more careful with layers including single-qubit gates. The reason for that is that the TEBD algorithm underlying the [`LayerwiseEvolver`](backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") must update even and odd bonds in an alternating manner. And because single-qubit gates are not applied on a site, but instead are split in half and applied to the bonds on either side, a layer of single-qubit gates acting on all qubits would break this assumption. +However, we must be more careful with layers including single-qubit gates. The reason for that is that the TEBD algorithm underlying the [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") must update even and odd bonds in an alternating manner. And because single-qubit gates are not applied on a site, but instead are split in half and applied to the bonds on either side, a layer of single-qubit gates acting on all qubits would break this assumption. To circumvent this problem, we can take any layer consisting of only single-qubit gates, and apply twice (once on the even and once on the odd bonds). @@ -129,7 +129,7 @@ Now that we know how to treat layers consisting of two-qubit and single-qubit ga >>> assert len(layer_models) == 8 ``` -In the end, we have 8 [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel")’s, one for each of the 4 two-qubit layers, and two for each of the 2 single-qubit layers. +In the end, we have 8 [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel")’s, one for each of the 4 two-qubit layers, and two for each of the 2 single-qubit layers. Finally, we can define our [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") protocol to be used within the [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse") function. @@ -147,7 +147,7 @@ Finally, we can define our [`ApproxEvolverFactory`](dynamic#approxevolverfactory It should be noted, that in this workflow we have not yet fixed the time step used by the Trotter formula. We have also only set up a single repetition of the Trotter formula as the rest will be done by the internal [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm, executed during [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse"). -Of course, you could also use this to specify a [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory"). But you can also mix-and-match a [`quimb_layers.LayerwiseEvolver`](backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") with a [`quimb_tebd.TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver"). +Of course, you could also use this to specify a [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory"). But you can also mix-and-match a [`quimb_layers.LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layerwise-evolver "qiskit_addon_mpf.backends.quimb_layers.LayerwiseEvolver") with a [`quimb_tebd.TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver"). ## Resources diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state.mdx index 87e1877a6d9..b01b343406e 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.quimb_tebd.MPOState # MPOState - Bases: [`MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_1d/index.html#quimb.tensor.tensor_1d.MatrixProductOperator), [`State`](backends#state "qiskit_addon_mpf.backends.interface.State") + Bases: [`MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_1d/index.html#quimb.tensor.tensor_1d.MatrixProductOperator), [`State`](/docs/api/qiskit-addon-mpf/backends#state "qiskit_addon_mpf.backends.interface.State") An MPO enforcing the Vidal gauge. - This specialization of quimb’s existing [`quimb.tensor.MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MatrixProductOperator) enforces the Vidal gauge throughout its existence. This ensures a stable behavior of the [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm when using the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver"). + This specialization of quimb’s existing [`quimb.tensor.MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MatrixProductOperator) enforces the Vidal gauge throughout its existence. This ensures a stable behavior of the [`DynamicMPF`](/docs/api/qiskit-addon-mpf/dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm when using the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver"). Initialize a [`MPOState`](#qiskit_addon_mpf.backends.quimb_tebd.MPOState "qiskit_addon_mpf.backends.quimb_tebd.MPOState") instance. @@ -56,7 +56,7 @@ python_api_name: qiskit_addon_mpf.backends.quimb_tebd.MPOState whether the gate should be applied to the lower (`conj=False`, the default, [`lower_ind()`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_arbgeom/index.html#quimb.tensor.tensor_arbgeom.TensorNetworkGenOperator.lower_ind)) or upper (`conj=True`, [`upper_ind()`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_arbgeom/index.html#quimb.tensor.tensor_arbgeom.TensorNetworkGenOperator.upper_ind)) indices of the underlying MPO. - This is essentially how the LHS and RHS of the [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") are differentiated, by passing their [`Evolver.conjugate`](backends#conjugate "qiskit_addon_mpf.backends.Evolver.conjugate") property to this argument. + This is essentially how the LHS and RHS of the [`DynamicMPF`](/docs/api/qiskit-addon-mpf/dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") are differentiated, by passing their [`Evolver.conjugate`](/docs/api/qiskit-addon-mpf/backends#conjugate "qiskit_addon_mpf.backends.Evolver.conjugate") property to this argument. * **split\_opts** – additional keyword arguments that will be forwarded to the [`quimb.tensor.tensor_split()`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.tensor_split) function. These can be used to affect the truncation of the tensor before it gets contracted back into the MPO. diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver.mdx index 0ce6eafbf5c..fb19f37dd90 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver # TEBDEvolver - Bases: [`TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_1d_tebd/index.html#quimb.tensor.tensor_1d_tebd.TEBD), [`Evolver`](backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") + Bases: [`TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_1d_tebd/index.html#quimb.tensor.tensor_1d_tebd.TEBD), [`Evolver`](/docs/api/qiskit-addon-mpf/backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") A TEBD algorithm for evolving an internal MPO. - As discussed in more detail in [`quimb_tebd`](backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd"), this extension of `quimb`’s existing [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) implementation time-evolves an internal matrix product operator (MPO) rather than the conventional matrix product state (MPS). + As discussed in more detail in [`quimb_tebd`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd#module-qiskit_addon_mpf.backends.quimb_tebd "qiskit_addon_mpf.backends.quimb_tebd"), this extension of `quimb`’s existing [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) implementation time-evolves an internal matrix product operator (MPO) rather than the conventional matrix product state (MPS). More concretely, the internal object is expected to be an [`MPOState`](backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState"). @@ -30,7 +30,7 @@ python_api_name: qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver a reference to the time-evolution state. This overwrites the `p0` argument of the underlying [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) class. - In contrast to the default behavior, this state will **NOT** be canonicalized. Instead, it is taken as is and is kept **by reference** (i.e. no copy is created). This ensures that the same object can be shared between two instances of this class, as required by the [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm. + In contrast to the default behavior, this state will **NOT** be canonicalized. Instead, it is taken as is and is kept **by reference** (i.e. no copy is created). This ensures that the same object can be shared between two instances of this class, as required by the [`DynamicMPF`](/docs/api/qiskit-addon-mpf/dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm. * **args** – any further positional arguments will be forwarded to the [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD) constructor. diff --git a/docs/api/qiskit-addon-mpf/backends-quimb-tebd.mdx b/docs/api/qiskit-addon-mpf/backends-quimb-tebd.mdx index 1b9e2f9a07d..a61216ec9c0 100644 --- a/docs/api/qiskit-addon-mpf/backends-quimb-tebd.mdx +++ b/docs/api/qiskit-addon-mpf/backends-quimb-tebd.mdx @@ -24,10 +24,10 @@ A [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#modu ``` -| | | -| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------- | -| [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") | A TEBD algorithm for evolving an internal MPO. | -| [`MPOState`](backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState") | An MPO enforcing the Vidal gauge. | +| | | +| ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | +| [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") | A TEBD algorithm for evolving an internal MPO. | +| [`MPOState`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState") | An MPO enforcing the Vidal gauge. | ## Underlying method @@ -38,13 +38,13 @@ The classes provided by this module serve two purposes: 1. Connecting [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#module-quimb)’s implementation to the interface set out by [`qiskit_addon_mpf.backends`](backends#module-qiskit_addon_mpf.backends "qiskit_addon_mpf.backends"). 2. Extending [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#module-quimb)’s TEBD implementation to handle an internal MPO (rather than MPS) state (see also [`State`](backends#state "qiskit_addon_mpf.backends.State") for more details). -In the simplest sense, this module provides a straight-forward extension of the TEBD algorithm to evolve an internal MPO state. As such, if you wish to use this backend for your dynamic MPF algorithm, you must encode the Hamiltonian that you wish to time-evolve, in a [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#module-quimb)-native form. To be more concrete, the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") class (which is a subclass of [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD)) works with a Hamiltonian in the form of a [`quimb.tensor.LocalHam1D`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.LocalHam1D). Quimb provides a number of convenience methods for constructing such Hamiltonians in its [`quimb.tensor.tensor_builder`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_builder/index.html#module-quimb.tensor.tensor_builder) module. If none of those fulfill your needs, you can consider using the [`LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") class which implements some conversion methods from Qiskit-native objects. +In the simplest sense, this module provides a straight-forward extension of the TEBD algorithm to evolve an internal MPO state. As such, if you wish to use this backend for your dynamic MPF algorithm, you must encode the Hamiltonian that you wish to time-evolve, in a [`quimb`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/index.html#module-quimb)-native form. To be more concrete, the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") class (which is a subclass of [`quimb.tensor.TEBD`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.TEBD)) works with a Hamiltonian in the form of a [`quimb.tensor.LocalHam1D`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.LocalHam1D). Quimb provides a number of convenience methods for constructing such Hamiltonians in its [`quimb.tensor.tensor_builder`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/tensor_builder/index.html#module-quimb.tensor.tensor_builder) module. If none of those fulfill your needs, you can consider using the [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") class which implements some conversion methods from Qiskit-native objects. ## Code example This section shows a simple example to get you started with using this backend. The example shows how to create the three factory functions required for the [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse"). -First, we create the `identity_factory` which has to match the [`IdentityStateFactory`](dynamic#identitystatefactory "qiskit_addon_mpf.dynamic.IdentityStateFactory") protocol. We do so simply by using the [`quimb.tensor.MPO_identity()`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MPO_identity) function and wrapping the resulting [`quimb.tensor.MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MatrixProductOperator) with our custom [`MPOState`](backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState") interface. +First, we create the `identity_factory` which has to match the [`IdentityStateFactory`](dynamic#identitystatefactory "qiskit_addon_mpf.dynamic.IdentityStateFactory") protocol. We do so simply by using the [`quimb.tensor.MPO_identity()`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MPO_identity) function and wrapping the resulting [`quimb.tensor.MatrixProductOperator`](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.MatrixProductOperator) with our custom [`MPOState`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-mpo-state "qiskit_addon_mpf.backends.quimb_tebd.MPOState") interface. ```python >>> from qiskit_addon_mpf.backends.quimb_tebd import MPOState @@ -60,7 +60,7 @@ Next, before being able to define the [`ExactEvolverFactory`](dynamic#exactevolv >>> hamil = ham_1d_heis(num_qubits, 0.8, 0.3, cyclic=False) ``` -We can now construct the exact and approximate time-evolution instance factories. To do so, we can simply use [`functools.partial()`](https://docs.python.org/3/library/functools.html#functools.partial) to bind the pre-defined values of the [`TEBDEvolver`](backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") initializer, reducing it to the correct interface as expected by the [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory") and [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") protocols, respectively. +We can now construct the exact and approximate time-evolution instance factories. To do so, we can simply use [`functools.partial()`](https://docs.python.org/3/library/functools.html#functools.partial) to bind the pre-defined values of the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-quimb-tebd-tebd-evolver "qiskit_addon_mpf.backends.quimb_tebd.TEBDEvolver") initializer, reducing it to the correct interface as expected by the [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory") and [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") protocols, respectively. ```python >>> from functools import partial diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model.mdx index bbde62cb3b1..6cc87c3dd32 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model.mdx @@ -22,7 +22,7 @@ python_api_name: qiskit_addon_mpf.backends.tenpy_layers.LayerModel Calculate the interaction Hamiltonian based on the coupling and onsite terms. - Essentially, this class overwrites [`calc_H_bond()`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.model.CouplingModel.html#tenpy.models.model.CouplingModel.calc_H_bond "(in TeNPy v1.0.5)") and takes care of removing even or odd bond interaction Hamiltonians depending on the value of `keep_only_odd` (see [`tenpy_layers`](backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers") for more details). + Essentially, this class overwrites [`calc_H_bond()`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.model.CouplingModel.html#tenpy.models.model.CouplingModel.calc_H_bond "(in TeNPy v1.0.5)") and takes care of removing even or odd bond interaction Hamiltonians depending on the value of `keep_only_odd` (see [`tenpy_layers`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers") for more details). **Parameters** diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver.mdx index fcdf88a3139..2f6bbc96aec 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver.mdx @@ -13,7 +13,7 @@ python_api_name: qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver A special case of the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") based on layer-wise evolution models. - As also explained in [`tenpy_layers`](backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers"), this implementation extracts the alternating even/odd bond updates implemented inside of the original [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)") to become the end users responsibility. It does so, by replacing the single Hamiltonian provided to the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") instance with a sequence of [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") instances. Every single instance of these encodes a single **layer** of interactions. These should enforce the alternating updates of even and odd bonds of the underlying tensor network. + As also explained in [`tenpy_layers`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers"), this implementation extracts the alternating even/odd bond updates implemented inside of the original [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)") to become the end users responsibility. It does so, by replacing the single Hamiltonian provided to the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") instance with a sequence of [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") instances. Every single instance of these encodes a single **layer** of interactions. These should enforce the alternating updates of even and odd bonds of the underlying tensor network. The motivation for this more complicated interface is that is provides a lot more flexbility and enables users to define custom Trotter product formulas rather than being limited to the ones implemented by TeNPy directly. @@ -22,7 +22,7 @@ python_api_name: qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver **Parameters** * **evolution\_state** ([*tenpy\_tebd.MPOState*](backends-tenpy-tebd-mpo-state "qiskit_addon_mpf.backends.tenpy_tebd.MPOState")) – forwarded to [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver"). Please refer to its documentation for more details. - * **layers** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*LayerModel*](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel")*]*) – the list of models describing single layers of interactions. See above as well as the explanations provided in [`tenpy_layers`](backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers"). + * **layers** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*LayerModel*](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel")*]*) – the list of models describing single layers of interactions. See above as well as the explanations provided in [`tenpy_layers`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers#module-qiskit_addon_mpf.backends.tenpy_layers "qiskit_addon_mpf.backends.tenpy_layers"). * **args** – any further positional arguments will be forwarded to the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") constructor. * **kwargs** – any further keyword arguments will be forwarded to the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") constructor. diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-layers.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-layers.mdx index f52a8edb563..58ac5ac3473 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-layers.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-layers.mdx @@ -28,10 +28,10 @@ A layer-wise time-evolution backend using [`tenpy`](https://tenpy.readthedocs.io ``` -| | | -| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [`LayerwiseEvolver`](backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") | A special case of the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") based on layer-wise evolution models. | -| [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") | A model for representing a layer of time-evolution interactions. | +| | | +| -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") | A special case of the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") based on layer-wise evolution models. | +| [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") | A model for representing a layer of time-evolution interactions. | ## Underlying method @@ -84,19 +84,19 @@ In the circuit above, we can clearly identify its layer-wise structure. We can e It is not possible to instruct TeNPy’s TEBD algorithm to simulate the exact structure of the circuit shown above. The reason for that is a limitation in its interface, as it only accepts the full Hamiltonian to be provided which is then time-evolved using pre-defined Trotter formulas. However, in doing so it does not treat the order of the Pauli terms in a Hamiltonian with any significance (like we do here). -If one wants to compute the dynamic MPF coefficients of a time-evolution employing a product formula structure other than the ones implemented in TeNPy (like the example above), then one can use the time-evolution algorithm provided by this module. Rather than taking a single monolithic Hamiltonian whose time-evolution is to be modeled, the [`LayerwiseEvolver`](backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") accepts a list of [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") objects, each one describing an individual layer of the product formula. This gives the user full flexibility in defining the Trotter decomposition down to the most granular level. +If one wants to compute the dynamic MPF coefficients of a time-evolution employing a product formula structure other than the ones implemented in TeNPy (like the example above), then one can use the time-evolution algorithm provided by this module. Rather than taking a single monolithic Hamiltonian whose time-evolution is to be modeled, the [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") accepts a list of [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") objects, each one describing an individual layer of the product formula. This gives the user full flexibility in defining the Trotter decomposition down to the most granular level. However, caution must be applied to ensure that the property of TEBD to update even and odd bonds in an alternating manner is still guaranteed. Luckily, for quantum circuits consisting of at most two-qubit gates, this property is satisfied by construction. ## Code example -In this section, we build up on the example above and show how to take a custom Trotter formula and use it to construct a [`LayerwiseEvolver`](backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") which can be used to replace the [`tenpy_tebd.TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") in the workflow described in [`tenpy_tebd`](backends-tenpy-tebd#module-qiskit_addon_mpf.backends.tenpy_tebd "qiskit_addon_mpf.backends.tenpy_tebd"). +In this section, we build up on the example above and show how to take a custom Trotter formula and use it to construct a [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") which can be used to replace the [`tenpy_tebd.TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") in the workflow described in [`tenpy_tebd`](backends-tenpy-tebd#module-qiskit_addon_mpf.backends.tenpy_tebd "qiskit_addon_mpf.backends.tenpy_tebd"). The overall workflow of using this module is the same as of the [`tenpy_tebd`](backends-tenpy-tebd#module-qiskit_addon_mpf.backends.tenpy_tebd "qiskit_addon_mpf.backends.tenpy_tebd") module, so be sure to read those instructions as well. -Simply put, we must convert each one of the circuit `layers` (see above) into a [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") instance. For this purpose, we can use its [`from_quantum_circuit()`](backends-tenpy-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.tenpy_layers.LayerModel.from_quantum_circuit") method. +Simply put, we must convert each one of the circuit `layers` (see above) into a [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") instance. For this purpose, we can use its [`from_quantum_circuit()`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.tenpy_layers.LayerModel.from_quantum_circuit") method. ```python >>> from qiskit_addon_mpf.backends.tenpy_layers import LayerModel @@ -106,7 +106,7 @@ Simply put, we must convert each one of the circuit `layers` (see above) into a In the code above you can see how simple the conversion is for layers which contain only two-qubit gates acting on mutually exclusive qubits (which layers of depth 1 guarantee). -However, we must be more careful with layers including single-qubit gates. The reason for that is that the TEBD algorithm underlying the [`LayerwiseEvolver`](backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") must update even and odd bonds in an alternating manner. And because single-qubit gates are not applied on a site, but instead are split in half and applied to the bonds on either side, a layer of single-qubit gates acting on all qubits would break this assumption. +However, we must be more careful with layers including single-qubit gates. The reason for that is that the TEBD algorithm underlying the [`LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") must update even and odd bonds in an alternating manner. And because single-qubit gates are not applied on a site, but instead are split in half and applied to the bonds on either side, a layer of single-qubit gates acting on all qubits would break this assumption. To circumvent this problem, we can take any layer consisting of only single-qubit gates, and apply it twice (once on the even and once on the odd bonds). @@ -133,7 +133,7 @@ Now that we know how to treat layers consisting of two-qubit and single-qubit ga >>> assert len(layer_models) == 8 ``` -In the end, we have 8 [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel")’s, one for each of the 4 two-qubit layers, and two for each of the 2 single-qubit layers. +In the end, we have 8 [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel")’s, one for each of the 4 two-qubit layers, and two for each of the 2 single-qubit layers. Finally, we can define our [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") protocol to be used within the [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse") function. @@ -158,7 +158,7 @@ Finally, we can define our [`ApproxEvolverFactory`](dynamic#approxevolverfactory It should be noted, that in this workflow we have not yet fixed the time step used by the Trotter formula. We have also only set up a single repetition of the Trotter formula as the rest will be done by the internal [`DynamicMPF`](dynamic#dynamicmpf "qiskit_addon_mpf.dynamic.DynamicMPF") algorithm, executed during [`setup_dynamic_lse()`](dynamic#setup_dynamic_lse "qiskit_addon_mpf.dynamic.setup_dynamic_lse"). -Of course, you could also use this to specify a [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory"). But you can also mix-and-match a [`tenpy_layers.LayerwiseEvolver`](backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") with a [`tenpy_tebd.TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver"). +Of course, you could also use this to specify a [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory"). But you can also mix-and-match a [`tenpy_layers.LayerwiseEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver") with a [`tenpy_tebd.TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver"). ## Resources diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state.mdx index ea6caf3e5e3..eaa129b2ad6 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.tenpy_tebd.MPOState # MPOState - Bases: [`MPO`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.networks.mpo.MPO.html#tenpy.networks.mpo.MPO "(in TeNPy v1.0.5)"), [`State`](backends#state "qiskit_addon_mpf.backends.interface.State") + Bases: [`MPO`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.networks.mpo.MPO.html#tenpy.networks.mpo.MPO "(in TeNPy v1.0.5)"), [`State`](/docs/api/qiskit-addon-mpf/backends#state "qiskit_addon_mpf.backends.interface.State") - A mediator class to make TeNPy’s MPO match the [`State`](backends#state "qiskit_addon_mpf.backends.State") interface. + A mediator class to make TeNPy’s MPO match the [`State`](/docs/api/qiskit-addon-mpf/backends#state "qiskit_addon_mpf.backends.State") interface. - This class simply ensures that a [`tenpy.networks.mpo.MPO`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.networks.mpo.MPO.html#tenpy.networks.mpo.MPO "(in TeNPy v1.0.5)") object can work as a [`State`](backends#state "qiskit_addon_mpf.backends.State") instance. + This class simply ensures that a [`tenpy.networks.mpo.MPO`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.networks.mpo.MPO.html#tenpy.networks.mpo.MPO "(in TeNPy v1.0.5)") object can work as a [`State`](/docs/api/qiskit-addon-mpf/backends#state "qiskit_addon_mpf.backends.State") instance. ## Methods diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver.mdx index fc8eff6ac9f..ebf748494a9 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver.mdx @@ -9,11 +9,11 @@ python_api_name: qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver # TEBDEvolver - Bases: [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)"), [`Evolver`](backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") + Bases: [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)"), [`Evolver`](/docs/api/qiskit-addon-mpf/backends#evolver "qiskit_addon_mpf.backends.interface.Evolver") A TEBD algorithm for evolving an internal MPO. - As discussed in more detail in [`tenpy_tebd`](backends-tenpy-tebd#module-qiskit_addon_mpf.backends.tenpy_tebd "qiskit_addon_mpf.backends.tenpy_tebd"), this extension of TeNPy’s existing [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)") implementation time-evolves an internal matrix product operator (MPO) rather than the conventional matrix product state (MPS). + As discussed in more detail in [`tenpy_tebd`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd#module-qiskit_addon_mpf.backends.tenpy_tebd "qiskit_addon_mpf.backends.tenpy_tebd"), this extension of TeNPy’s existing [`TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)") implementation time-evolves an internal matrix product operator (MPO) rather than the conventional matrix product state (MPS). More concretely, the internal object is expected to be an [`MPOState`](backends-tenpy-tebd-mpo-state "qiskit_addon_mpf.backends.tenpy_tebd.MPOState"). diff --git a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd.mdx b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd.mdx index c70393df874..c97b8296d23 100644 --- a/docs/api/qiskit-addon-mpf/backends-tenpy-tebd.mdx +++ b/docs/api/qiskit-addon-mpf/backends-tenpy-tebd.mdx @@ -28,11 +28,11 @@ A [`tenpy`](https://tenpy.readthedocs.io/en/latest/main.html#module-tenpy "(in T ``` -| | | -| ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | -| [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") | A TEBD algorithm for evolving an internal MPO. | -| [`MPOState`](backends-tenpy-tebd-mpo-state "qiskit_addon_mpf.backends.tenpy_tebd.MPOState") | A mediator class to make TeNPy's MPO match the [`State`](backends#state "qiskit_addon_mpf.backends.State") interface. | -| [`MPS_neel_state`](backends-tenpy-tebd-mps-neel-state "qiskit_addon_mpf.backends.tenpy_tebd.MPS_neel_state") | Constructs the Néel state as an MPS. | +| | | +| --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") | A TEBD algorithm for evolving an internal MPO. | +| [`MPOState`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state "qiskit_addon_mpf.backends.tenpy_tebd.MPOState") | A mediator class to make TeNPy's MPO match the [`State`](backends#state "qiskit_addon_mpf.backends.State") interface. | +| [`MPS_neel_state`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mps-neel-state "qiskit_addon_mpf.backends.tenpy_tebd.MPS_neel_state") | Constructs the Néel state as an MPS. | ## Underlying method @@ -43,7 +43,7 @@ The classes provided by this module serve two purposes: 1. Connecting [`tenpy`](https://tenpy.readthedocs.io/en/latest/main.html#module-tenpy "(in TeNPy v1.0.5)")’s implementation to the interface set out by [`qiskit_addon_mpf.backends`](backends#module-qiskit_addon_mpf.backends "qiskit_addon_mpf.backends"). 2. Extending [`tenpy`](https://tenpy.readthedocs.io/en/latest/main.html#module-tenpy "(in TeNPy v1.0.5)")’s TEBD implementation to handle an internal MPO (rather than MPS) state (see also [`State`](backends#state "qiskit_addon_mpf.backends.State") for more details). -In the simplest sense, this module provides a straight-forward extension of the TEBD algorithm to evolve an internal MPO state. As such, if you wish to use this backend for your dynamic MPF algorithm, you must encode the Hamiltonian that you wish to time-evolve, in a [`tenpy`](https://tenpy.readthedocs.io/en/latest/main.html#module-tenpy "(in TeNPy v1.0.5)")-native form. To be more concrete, the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") class (which is a subclass of [`tenpy.algorithms.tebd.TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)")) works with a Hamiltonian in the form of a [`Model`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.model.Model.html#tenpy.models.model.Model "(in TeNPy v1.0.5)"). TeNPy provides a number of convenience methods for constructing such Hamiltonians in its [`tenpy.models`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.html#module-tenpy.models "(in TeNPy v1.0.5)") module. If none of those fulfill your needs, you can consider using the [`LayerModel`](backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") class which implements some conversion methods from Qiskit-native objects. +In the simplest sense, this module provides a straight-forward extension of the TEBD algorithm to evolve an internal MPO state. As such, if you wish to use this backend for your dynamic MPF algorithm, you must encode the Hamiltonian that you wish to time-evolve, in a [`tenpy`](https://tenpy.readthedocs.io/en/latest/main.html#module-tenpy "(in TeNPy v1.0.5)")-native form. To be more concrete, the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") class (which is a subclass of [`tenpy.algorithms.tebd.TEBDEngine`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.tebd.TEBDEngine.html#tenpy.algorithms.tebd.TEBDEngine "(in TeNPy v1.0.5)")) works with a Hamiltonian in the form of a [`Model`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.model.Model.html#tenpy.models.model.Model "(in TeNPy v1.0.5)"). TeNPy provides a number of convenience methods for constructing such Hamiltonians in its [`tenpy.models`](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.html#module-tenpy.models "(in TeNPy v1.0.5)") module. If none of those fulfill your needs, you can consider using the [`LayerModel`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layer-model "qiskit_addon_mpf.backends.tenpy_layers.LayerModel") class which implements some conversion methods from Qiskit-native objects. ## Code example @@ -73,7 +73,7 @@ Next, we can create the `identity_factory` which has to match the [`IdentityStat >>> identity_factory = partial(MPOState.initialize_from_lattice, hamil.lat), ``` -We can now construct the [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory") and [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") time-evolution instance factories. To do so, we can simply bind the pre-defined values of the [`TEBDEvolver`](backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") initializer, reducing it to the correct interface as expected by the respective function protocols. +We can now construct the [`ExactEvolverFactory`](dynamic#exactevolverfactory "qiskit_addon_mpf.dynamic.ExactEvolverFactory") and [`ApproxEvolverFactory`](dynamic#approxevolverfactory "qiskit_addon_mpf.dynamic.ApproxEvolverFactory") time-evolution instance factories. To do so, we can simply bind the pre-defined values of the [`TEBDEvolver`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-tebd-evolver "qiskit_addon_mpf.backends.tenpy_tebd.TEBDEvolver") initializer, reducing it to the correct interface as expected by the respective function protocols. ```python >>> from qiskit_addon_mpf.backends.tenpy_tebd import TEBDEvolver diff --git a/docs/api/qiskit-addon-mpf/release-notes.mdx b/docs/api/qiskit-addon-mpf/release-notes.mdx index 549395975f5..0010177e33f 100644 --- a/docs/api/qiskit-addon-mpf/release-notes.mdx +++ b/docs/api/qiskit-addon-mpf/release-notes.mdx @@ -20,9 +20,9 @@ in_page_toc_max_heading_level: 2 ### New Features -* TeNPy can disable `Sz` conservation. Previously, the [`initialize_from_lattice()`](backends-tenpy-tebd-mpo-state#initialize_from_lattice "qiskit_addon_mpf.backends.tenpy_tebd.MPOState.initialize_from_lattice") method did not support this, but a new keyword argument `conserve` has been added which allows the disabling of `Sz` conservation. The default remains to be `True`. +* TeNPy can disable `Sz` conservation. Previously, the [`initialize_from_lattice()`](/docs/api/qiskit-addon-mpf/backends-tenpy-tebd-mpo-state#initialize_from_lattice "qiskit_addon_mpf.backends.tenpy_tebd.MPOState.initialize_from_lattice") method did not support this, but a new keyword argument `conserve` has been added which allows the disabling of `Sz` conservation. The default remains to be `True`. -* Guards against unexpected behavior when `N_steps != 1` in [`qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver.evolve()`](backends-tenpy-layers-layerwise-evolver#evolve "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver.evolve"). +* Guards against unexpected behavior when `N_steps != 1` in [`qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver.evolve()`](/docs/api/qiskit-addon-mpf/backends-tenpy-layers-layerwise-evolver#evolve "qiskit_addon_mpf.backends.tenpy_layers.LayerwiseEvolver.evolve"). @@ -32,7 +32,7 @@ in_page_toc_max_heading_level: 2 * This package is now compatible with Qiskit SDK 2.0. -* The `keep_only_odd` attribute of [`quimb_layers.LayerModel`](backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") has been removed. The internal workings have been refactored to ensure that data reported by its `terms` attribute (which is inherited from the base class) is already taking the `keep_only_odd` argument of the [`quimb_layers.LayerModel.from_quantum_circuit()`](backends-quimb-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.quimb_layers.LayerModel.from_quantum_circuit") constructor method. +* The `keep_only_odd` attribute of [`quimb_layers.LayerModel`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model "qiskit_addon_mpf.backends.quimb_layers.LayerModel") has been removed. The internal workings have been refactored to ensure that data reported by its `terms` attribute (which is inherited from the base class) is already taking the `keep_only_odd` argument of the [`quimb_layers.LayerModel.from_quantum_circuit()`](/docs/api/qiskit-addon-mpf/backends-quimb-layers-layer-model#from_quantum_circuit "qiskit_addon_mpf.backends.quimb_layers.LayerModel.from_quantum_circuit") constructor method. * The `scaling_factor` keyword argument of the `from_quantum_circuit` constructor methods has been removed. It is not actually needed and was merely adding an additional (confusing) re-scaling. @@ -40,7 +40,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixes the [`DynamicMPF.evolve()`](dynamic#evolve "qiskit_addon_mpf.dynamic.DynamicMPF.evolve") method when providing `time` values with numerical inaccuracies (e.g. `time=1.00000001`). Previously this could cause the LHS and RHS time evolvers to advance too far. The new default accuracy is 8 decimal places, but it may be configured via the [`DynamicMPF.TIME_DECIMALS`](dynamic#time_decimals "qiskit_addon_mpf.dynamic.DynamicMPF.TIME_DECIMALS") value. +* Fixes the [`DynamicMPF.evolve()`](/docs/api/qiskit-addon-mpf/dynamic#evolve "qiskit_addon_mpf.dynamic.DynamicMPF.evolve") method when providing `time` values with numerical inaccuracies (e.g. `time=1.00000001`). Previously this could cause the LHS and RHS time evolvers to advance too far. The new default accuracy is 8 decimal places, but it may be configured via the [`DynamicMPF.TIME_DECIMALS`](/docs/api/qiskit-addon-mpf/dynamic#time_decimals "qiskit_addon_mpf.dynamic.DynamicMPF.TIME_DECIMALS") value. @@ -54,7 +54,7 @@ in_page_toc_max_heading_level: 2 ### New Features -* Adds the ability to compute dynamic (i.e. time-dependent) MPF coefficients. For more details, refer to [`qiskit_addon_mpf.dynamic`](dynamic#module-qiskit_addon_mpf.dynamic "qiskit_addon_mpf.dynamic"). +* Adds the ability to compute dynamic (i.e. time-dependent) MPF coefficients. For more details, refer to [`qiskit_addon_mpf.dynamic`](/docs/api/qiskit-addon-mpf/dynamic#module-qiskit_addon_mpf.dynamic "qiskit_addon_mpf.dynamic"). diff --git a/docs/api/qiskit-addon-mthree/_package.json b/docs/api/qiskit-addon-mthree/_package.json new file mode 100644 index 00000000000..878038d00ce --- /dev/null +++ b/docs/api/qiskit-addon-mthree/_package.json @@ -0,0 +1,4 @@ +{ + "name": "qiskit-addon-mthree", + "version": "3.0.0" +} diff --git a/docs/api/qiskit-addon-mthree/_toc.json b/docs/api/qiskit-addon-mthree/_toc.json new file mode 100644 index 00000000000..ed4eca1f671 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/_toc.json @@ -0,0 +1,93 @@ +{ + "title": "Matrix-free Measurement Mitigation (M3)", + "children": [ + { + "title": "API index", + "url": "/docs/api/qiskit-addon-mthree" + }, + { + "title": "mthree", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-mthree" + }, + { + "title": "M3Mitigation", + "url": "/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation", + "untranslatable": true + } + ] + }, + { + "title": "mthree.classes", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-mthree/classes" + }, + { + "title": "ProbCollection", + "url": "/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection", + "untranslatable": true + }, + { + "title": "ProbDistribution", + "url": "/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution", + "untranslatable": true + }, + { + "title": "QuasiCollection", + "url": "/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection", + "untranslatable": true + }, + { + "title": "QuasiDistribution", + "url": "/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution", + "untranslatable": true + } + ] + }, + { + "title": "mthree.utils", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-mthree/utils" + }, + { + "title": "expval", + "url": "/docs/api/qiskit-addon-mthree/mthree-utils-expval", + "untranslatable": true + }, + { + "title": "expval_and_stddev", + "url": "/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev", + "untranslatable": true + }, + { + "title": "final_measurement_mapping", + "url": "/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping", + "untranslatable": true + }, + { + "title": "marginal_distribution", + "url": "/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution", + "untranslatable": true + }, + { + "title": "stddev", + "url": "/docs/api/qiskit-addon-mthree/mthree-utils-stddev", + "untranslatable": true + } + ] + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-mthree", + "parentLabel": "Matrix-free Measurement Mitigation (M3)" +} diff --git a/docs/api/qiskit-addon-mthree/classes.mdx b/docs/api/qiskit-addon-mthree/classes.mdx new file mode 100644 index 00000000000..a08762e654b --- /dev/null +++ b/docs/api/qiskit-addon-mthree/classes.mdx @@ -0,0 +1,26 @@ +--- +title: classes (latest version) +description: API reference for mthree.classes in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: mthree.classes +--- + + + + + +# Distributions + +| | | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | +| [`QuasiDistribution`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution "mthree.classes.QuasiDistribution")(data\[, shots, ...]) | A dict-like class for representing quasi-probabilities. | +| [`ProbDistribution`](/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution "mthree.classes.ProbDistribution")(data\[, shots, ...]) | A generic dict-like class for probability distributions. | + +# Distribution collections + +| | | +| ------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| [`QuasiCollection`](/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection "mthree.classes.QuasiCollection")(data) | QuasiCollection constructor. | +| [`ProbCollection`](/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection "mthree.classes.ProbCollection")(data) | ProbCollection constructor. | + diff --git a/docs/api/qiskit-addon-mthree/index.mdx b/docs/api/qiskit-addon-mthree/index.mdx new file mode 100644 index 00000000000..02e506236db --- /dev/null +++ b/docs/api/qiskit-addon-mthree/index.mdx @@ -0,0 +1,15 @@ +--- +title: Matrix-free Measurement Mitigation (M3) API documentation (latest version) +description: Index of all the modules in the latest version of qiskit-addon-mthree. +--- + + + + + +# Mitigation classes + +| | | +| ----------------------------------------------------------------------------------------------------------------------- | -------------------------- | +| [`M3Mitigation`](/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation "mthree.M3Mitigation")(\[system, iter\_threshold]) | Main M3 calibration class. | + diff --git a/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection.mdx b/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection.mdx new file mode 100644 index 00000000000..53518b4cb77 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-classes-prob-collection.mdx @@ -0,0 +1,133 @@ +--- +title: ProbCollection (latest version) +description: API reference for mthree.classes.ProbCollection in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: mthree.classes.ProbCollection +--- + + + +# mthree.classes.ProbCollection + + + ProbCollection constructor. + + **Parameters** + + **data** (*list or* [*ProbCollection*](#mthree.classes.ProbCollection "mthree.classes.ProbCollection")) – List of ProbDistribution instances. + + **Raises** + + **TypeError** – Must be list of ProbDistribution only. + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | + | [`expval`](#mthree.classes.ProbCollection.expval "mthree.classes.ProbCollection.expval")(\[exp\_ops]) | Expectation value over entire collection. | + | [`expval_and_stddev`](#mthree.classes.ProbCollection.expval_and_stddev "mthree.classes.ProbCollection.expval_and_stddev")(\[exp\_ops]) | Expectation value and standard deviation over entire collection. | + | [`stddev`](#mthree.classes.ProbCollection.stddev "mthree.classes.ProbCollection.stddev")() | Standard deviation over entire collection. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | + | [`mitigation_overhead`](#mthree.classes.ProbCollection.mitigation_overhead "mthree.classes.ProbCollection.mitigation_overhead") | Mitigation overhead over entire collection. | + | [`shots`](#mthree.classes.ProbCollection.shots "mthree.classes.ProbCollection.shots") | Number of shots taken over collection. | + + ### expval + + + Expectation value over entire collection. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – Diagonal operators over which to compute expval. + + **Returns** + + Array of expectation values. + + **Return type** + + ndarray + + **Raises** + + **M3Error** – Length of passes operators does not match container length. + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### expval\_and\_stddev + + + Expectation value and standard deviation over entire collection. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – Diagonal operators over which to compute expval. + + **Returns** + + Tuples of expval and stddev pairs. + + **Return type** + + list + + **Raises** + + **M3Error** – Length of passes operators does not match container length. + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### mitigation\_overhead + + + Mitigation overhead over entire collection. + + **Returns** + + Array of mitigation overhead values. + + **Return type** + + ndarray + + + ### shots + + + Number of shots taken over collection. + + **Returns** + + Array of shots values. + + **Return type** + + ndarray + + + ### stddev + + + Standard deviation over entire collection. + + **Returns** + + Array of standard deviations. + + **Return type** + + ndarray + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution.mdx b/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution.mdx new file mode 100644 index 00000000000..4bae199554a --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-classes-prob-distribution.mdx @@ -0,0 +1,103 @@ +--- +title: ProbDistribution (latest version) +description: API reference for mthree.classes.ProbDistribution in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: mthree.classes.ProbDistribution +--- + + + +# mthree.classes.ProbDistribution + + + A generic dict-like class for probability distributions. + + **Parameters** + + * **data** (*dict or Counts or* [*ProbDistribution*](#mthree.classes.ProbDistribution "mthree.classes.ProbDistribution") *or*[*QuasiDistribution*](mthree-classes-quasi-distribution "mthree.classes.QuasiDistribution")) – Input data. + * **shots** (*int*) – Number shots taken to form distribution. + + **Raises** + + **M3Error** – Input not derived from discrete samples. + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | + | [`expval`](#mthree.classes.ProbDistribution.expval "mthree.classes.ProbDistribution.expval")(\[exp\_ops]) | Compute expectation value from distribution. | + | [`expval_and_stddev`](#mthree.classes.ProbDistribution.expval_and_stddev "mthree.classes.ProbDistribution.expval_and_stddev")(\[exp\_ops]) | Compute expectation value and standard deviation from distribution. | + | [`stddev`](#mthree.classes.ProbDistribution.stddev "mthree.classes.ProbDistribution.stddev")() | Compute standard deviation from distribution. | + + ### expval + + + Compute expectation value from distribution. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – String representation of diagonal qubit operators used in computing the expectation value. + + **Returns** + + Expectation value. + + **Return type** + + float + + **Raises** + + **M3Error** – Invalid type passed to exp\_ops + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### expval\_and\_stddev + + + Compute expectation value and standard deviation from distribution. + + **Parameters** + + **exp\_ops** (*str or dict*) – String or dict representation of diagonal qubit operators used in computing the expectation value. + + **Returns** + + Expectation value. float: Standard deviation. + + **Return type** + + float + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### stddev + + + Compute standard deviation from distribution. + + **Returns** + + Standard deviation. + + **Return type** + + float + + **Raises** + + **M3Error** – Distribution is missing info. + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection.mdx b/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection.mdx new file mode 100644 index 00000000000..e57211b2556 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-classes-quasi-collection.mdx @@ -0,0 +1,148 @@ +--- +title: QuasiCollection (latest version) +description: API reference for mthree.classes.QuasiCollection in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: mthree.classes.QuasiCollection +--- + + + +# mthree.classes.QuasiCollection + + + QuasiCollection constructor. + + **Parameters** + + **data** (*list or* [*QuasiCollection*](#mthree.classes.QuasiCollection "mthree.classes.QuasiCollection")) – List of QuasiDistribution instances. + + **Raises** + + **TypeError** – Must be list of QuasiDistribution only. + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | + | [`expval`](#mthree.classes.QuasiCollection.expval "mthree.classes.QuasiCollection.expval")(\[exp\_ops]) | Expectation value over entire collection. | + | [`expval_and_stddev`](#mthree.classes.QuasiCollection.expval_and_stddev "mthree.classes.QuasiCollection.expval_and_stddev")(\[exp\_ops]) | Expectation value and standard deviation over entire collection. | + | [`nearest_probability_distribution`](#mthree.classes.QuasiCollection.nearest_probability_distribution "mthree.classes.QuasiCollection.nearest_probability_distribution")() | Nearest probability distribution over collection | + | [`stddev`](#mthree.classes.QuasiCollection.stddev "mthree.classes.QuasiCollection.stddev")() | Standard deviation over entire collection. | + + ## Attributes + + | | | + | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | + | [`mitigation_overhead`](#mthree.classes.QuasiCollection.mitigation_overhead "mthree.classes.QuasiCollection.mitigation_overhead") | Mitigation overhead over entire collection. | + | [`shots`](#mthree.classes.QuasiCollection.shots "mthree.classes.QuasiCollection.shots") | Number of shots taken over collection. | + + ### expval + + + Expectation value over entire collection. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – Diagonal operators over which to compute expval. + + **Returns** + + Array of expectation values. + + **Return type** + + ndarray + + **Raises** + + **M3Error** – Length of passes operators does not match container length. + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### expval\_and\_stddev + + + Expectation value and standard deviation over entire collection. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – Diagonal operators over which to compute expval. + + **Returns** + + Tuples of expval and stddev pairs. + + **Return type** + + list + + **Raises** + + **M3Error** – Length of passes operators does not match container length. + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### mitigation\_overhead + + + Mitigation overhead over entire collection. + + **Returns** + + Array of mitigation overhead values. + + **Return type** + + ndarray + + + ### nearest\_probability\_distribution + + + Nearest probability distribution over collection + + **Returns** + + Collection of ProbDistributions. + + **Return type** + + [ProbCollection](mthree-classes-prob-collection "mthree.classes.ProbCollection") + + + ### shots + + + Number of shots taken over collection. + + **Returns** + + Array of shots values. + + **Return type** + + ndarray + + + ### stddev + + + Standard deviation over entire collection. + + **Returns** + + Array of standard deviations. + + **Return type** + + ndarray + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution.mdx b/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution.mdx new file mode 100644 index 00000000000..05747fc7940 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-classes-quasi-distribution.mdx @@ -0,0 +1,115 @@ +--- +title: QuasiDistribution (latest version) +description: API reference for mthree.classes.QuasiDistribution in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: mthree.classes.QuasiDistribution +--- + + + +# mthree.classes.QuasiDistribution + + + A dict-like class for representing quasi-probabilities. + + **Parameters** + + * **data** (*dict*) – Input data. + * **shots** (*int*) – Number shots taken to form quasi-distribution. + * **mitigation\_overhead** (*float*) – Overhead from performing mitigation. + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | + | [`expval`](#mthree.classes.QuasiDistribution.expval "mthree.classes.QuasiDistribution.expval")(\[exp\_ops]) | Compute expectation value from distribution. | + | [`expval_and_stddev`](#mthree.classes.QuasiDistribution.expval_and_stddev "mthree.classes.QuasiDistribution.expval_and_stddev")(\[exp\_ops]) | Compute expectation value and standard deviation estimate from distribution. | + | [`nearest_probability_distribution`](#mthree.classes.QuasiDistribution.nearest_probability_distribution "mthree.classes.QuasiDistribution.nearest_probability_distribution")(\[...]) | Takes a quasiprobability distribution and maps it to the closest probability distribution as defined by the L2-norm. | + | [`stddev`](#mthree.classes.QuasiDistribution.stddev "mthree.classes.QuasiDistribution.stddev")() | Compute standard deviation estimate from distribution. | + + ### expval + + + Compute expectation value from distribution. + + **Parameters** + + **exp\_ops** (*str or dict or list*) – String or dict representation of diagonal qubit operators used in computing the expectation value. + + **Returns** + + Expectation value. + + **Return type** + + float + + **Raises** + + **M3Error** – Invalid type passed to exp\_ops. + + + ### expval\_and\_stddev + + + Compute expectation value and standard deviation estimate from distribution. + + **Parameters** + + **exp\_ops** (*str or dict*) – String or dict representation of diagonal qubit operators used in computing the expectation value. + + **Returns** + + Expectation value. float: Estimate of standard deviation upper-bound. + + **Return type** + + float + + **Notes** + + The dict operator format is a sparse diagonal format using bitstrings as the keys. + + + ### nearest\_probability\_distribution + + + Takes a quasiprobability distribution and maps it to the closest probability distribution as defined by the L2-norm. + + **Parameters** + + **return\_distance** (*bool*) – Return the L2 distance between distributions. + + **Returns** + + Nearest probability distribution. float: Euclidean (L2) distance of distributions. + + **Return type** + + [ProbDistribution](mthree-classes-prob-distribution "mthree.classes.ProbDistribution") + + **Notes** + + Method from Smolin et al., Phys. Rev. Lett. 108, 070502 (2012). + + + ### stddev + + + Compute standard deviation estimate from distribution. + + **Returns** + + Estimate of standard deviation upper-bound. + + **Return type** + + float + + **Raises** + + **M3Error** – Missing shots or mitigation\_overhead information. + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation.mdx b/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation.mdx new file mode 100644 index 00000000000..de8461034e5 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-m-3-mitigation.mdx @@ -0,0 +1,276 @@ +--- +title: M3Mitigation (latest version) +description: API reference for mthree.M3Mitigation in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: mthree.M3Mitigation +--- + + + +# mthree.M3Mitigation + + + Main M3 calibration class. + + **Parameters** + + * **system** (*Backend*) – Target backend. + * **iter\_threshold** (*int*) – Sets the bitstring count at which iterative mode is turned on (assuming reasonable error rates). + + ### system + + + The target system or execution manager. + + **Type** + + Backend + + + ### system\_info + + + Information needed about the system + + **Type** + + dict + + + ### cal\_method + + + Calibration method used + + **Type** + + str + + + ### cal\_timestamp + + + Time at which cals were taken + + **Type** + + str + + + ### single\_qubit\_cals + + + 1Q calibration matrices + + **Type** + + list + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | + | [`apply_correction`](#mthree.M3Mitigation.apply_correction "mthree.M3Mitigation.apply_correction")(counts, qubits\[, distance, ...]) | Applies correction to given counts. | + | [`cals_from_file`](#mthree.M3Mitigation.cals_from_file "mthree.M3Mitigation.cals_from_file")(cals\_file) | Generated the calibration data from a previous runs output | + | [`cals_from_matrices`](#mthree.M3Mitigation.cals_from_matrices "mthree.M3Mitigation.cals_from_matrices")(matrices) | Init calibration data from list of NumPy arrays. | + | [`cals_from_system`](#mthree.M3Mitigation.cals_from_system "mthree.M3Mitigation.cals_from_system")(\[qubits, shots, method, ...]) | Grab calibration data from system. | + | [`cals_to_file`](#mthree.M3Mitigation.cals_to_file "mthree.M3Mitigation.cals_to_file")(\[cals\_file]) | Save calibration data to JSON file. | + | [`cals_to_matrices`](#mthree.M3Mitigation.cals_to_matrices "mthree.M3Mitigation.cals_to_matrices")() | Return single qubit cals as list of NumPy arrays | + | [`readout_fidelity`](#mthree.M3Mitigation.readout_fidelity "mthree.M3Mitigation.readout_fidelity")(\[qubits]) | Compute readout fidelity for calibrated qubits. | + | [`reduced_cal_matrix`](#mthree.M3Mitigation.reduced_cal_matrix "mthree.M3Mitigation.reduced_cal_matrix")(counts, qubits\[, distance]) | Return the reduced calibration matrix used in the solution. | + | [`tensored_cals_from_file`](#mthree.M3Mitigation.tensored_cals_from_file "mthree.M3Mitigation.tensored_cals_from_file")(cals\_file) | Generated the tensored calibration data from a previous runs output | + | [`tensored_cals_from_system`](#mthree.M3Mitigation.tensored_cals_from_system "mthree.M3Mitigation.tensored_cals_from_system")(\[qubits, shots, ...]) | Grab calibration data from system. | + + ### apply\_correction + + + Applies correction to given counts. + + **Parameters** + + * **counts** (*dict, list*) – Input counts dict or list of dicts. + * **qubits** (*dict, array\_like*) – Qubits on which measurements applied. + * **distance** (*int*) – Distance to correct for. Default=num\_bits + * **method** (*str*) – Solution method: ‘auto’, ‘direct’ or ‘iterative’. + * **max\_iter** (*int*) – Max. number of iterations, Default=25. + * **tol** (*float*) – Convergence tolerance of iterative method, Default=1e-4. + * **return\_mitigation\_overhead** (*bool*) – Returns the mitigation overhead, default=False. + * **details** (*bool*) – Return extra info, default=False. + + **Returns** + + **Dictionary of quasiprobabilities if** + + input is a single dict, else a collection of quasiprobabilities. + + **Return type** + + [QuasiDistribution](mthree-classes-quasi-distribution "mthree.classes.QuasiDistribution") or [QuasiCollection](mthree-classes-quasi-collection "mthree.classes.QuasiCollection") + + **Raises** + + **M3Error** – Bitstring length does not match number of qubits given. + + + ### cals\_from\_file + + + Generated the calibration data from a previous runs output + + **Parameters** + + **cals\_file** (*str*) – A string path to the saved counts file from an earlier run. + + **Raises** + + **M3Error** – Calibration in progress. + + + ### cals\_from\_matrices + + + Init calibration data from list of NumPy arrays. + + Missing entries are set to None elements. + + **Parameters** + + **matrices** (*list\_like*) – List of cals as NumPy arrays + + **Raises** + + **M3Error** – If system set error if list length != num\_qubits on system + + + ### cals\_from\_system + + + Grab calibration data from system. + + **Parameters** + + * **qubits** (*array\_like*) – Qubits over which to correct calibration data. Default is all. + * **shots** (*int*) – Number of shots per circuit. min(1e4, max\_shots). + * **method** (*str*) – Type of calibration, ‘balanced’ (default for hardware), ‘independent’ (default for simulators), or ‘marginal’. + * **initial\_reset** (*bool*) – Use resets at beginning of calibration circuits, default=False. + * **rep\_delay** (*float*) – Delay between circuits on IBM Quantum backends. + * **cals\_file** (*str*) – Output path to write JSON calibration data to. + * **async\_cal** (*bool*) – Do calibration async in a separate thread, default is True. + * **runtime\_mode** (*Batch or Session*) – Mode to run jobs in if using IBM system, default=None + + **Returns** + + List of jobs submitted. + + **Return type** + + list + + **Raises** + + **M3Error** – Called while a calibration currently in progress. + + + ### cals\_to\_file + + + Save calibration data to JSON file. + + **Parameters** + + **cals\_file** (*str*) – File in which to store calibrations. + + **Raises** + + * **M3Error** – Calibration filename missing. + * **M3Error** – Mitigator is not calibrated. + + + ### cals\_to\_matrices + + + Return single qubit cals as list of NumPy arrays + + **Returns** + + List of cals as NumPy arrays + + **Return type** + + list + + + ### readout\_fidelity + + + Compute readout fidelity for calibrated qubits. + + **Parameters** + + **qubits** (*array\_like*) – Qubits to compute over, default is all. + + **Returns** + + List of qubit fidelities. + + **Return type** + + list + + **Raises** + + * **M3Error** – Mitigator is not calibrated. + * **M3Error** – Qubit indices out of range. + + + ### reduced\_cal\_matrix + + + Return the reduced calibration matrix used in the solution. + + **Parameters** + + * **counts** (*dict*) – Input counts dict. + * **qubits** (*array\_like*) – Qubits on which measurements applied. + * **distance** (*int*) – Distance to correct for. Default=num\_bits + + **Returns** + + 2D array of reduced calibrations. dict: Counts in order they are displayed in matrix. + + **Return type** + + ndarray + + **Raises** + + **M3Error** – If bit-string length does not match passed number of qubits. + + + ### tensored\_cals\_from\_file + + + Generated the tensored calibration data from a previous runs output + + **Parameters** + + **cals\_file** (*str*) – A string path to the saved counts file from an earlier run. + + + ### tensored\_cals\_from\_system + + + Grab calibration data from system. + + **Parameters** + + * **qubits** (*array\_like*) – Qubits over which to correct calibration data. Default is all. + * **shots** (*int*) – Number of shots per circuit. Default is min(1e4, max\_shots). + * **method** (*str*) – Type of calibration, ‘balanced’ (default), ‘independent’, or ‘marginal’. + * **rep\_delay** (*float*) – Delay between circuits on IBM Quantum backends. + * **cals\_file** (*str*) – Output path to write JSON calibration data to. + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev.mdx b/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev.mdx new file mode 100644 index 00000000000..85b477d837c --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev.mdx @@ -0,0 +1,18 @@ +--- +title: expval_and_stddev (latest version) +description: API reference for mthree.utils.expval_and_stddev in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: mthree.utils.expval_and_stddev +--- + + + +# mthree.utils.expval\_and\_stddev + + + Compute expectation values from distributions. + + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-utils-expval.mdx b/docs/api/qiskit-addon-mthree/mthree-utils-expval.mdx new file mode 100644 index 00000000000..da840327fdb --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-utils-expval.mdx @@ -0,0 +1,18 @@ +--- +title: expval (latest version) +description: API reference for mthree.utils.expval in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: mthree.utils.expval +--- + + + +# mthree.utils.expval + + + Compute expectation values from distributions. + + + + diff --git a/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping.mdx b/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping.mdx new file mode 100644 index 00000000000..827467909df --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping.mdx @@ -0,0 +1,30 @@ +--- +title: final_measurement_mapping (latest version) +description: API reference for mthree.utils.final_measurement_mapping in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: mthree.utils.final_measurement_mapping +--- + + + +# mthree.utils.final\_measurement\_mapping + + + Return the final measurement mapping for the circuit. + + Dict keys label measured qubits, whereas the values indicate the classical bit onto which that qubits measurement result is stored. + + **Parameters** + + **circuit** (*QuantumCircuit or list*) – Input Qiskit QuantumCircuit or circuits. + + **Returns** + + Mapping of classical bits to qubits for final measurements. + + **Return type** + + dict or list + + diff --git a/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution.mdx b/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution.mdx new file mode 100644 index 00000000000..eb5f3728094 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution.mdx @@ -0,0 +1,39 @@ +--- +title: marginal_distribution (latest version) +description: API reference for mthree.utils.marginal_distribution in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: mthree.utils.marginal_distribution +--- + + + +# mthree.utils.marginal\_distribution + + + Grab the marginal counts from a given distribution. + + If an operator is passed for the indices then the position of the non-identity elements in the string will be used to set the indices to marginalize over. + + The mapping passed will be marginalized so that it can be directly used in applying the correction. The type of mapping at output is the same as that input. + + **Parameters** + + * **dist** (*dict*) – Input distribution + * **indices** (*array\_like or str*) – Indices (qubits) to keep or operator string + * **mapping** (*dict or array\_like*) – Optional, final measurement mapping. + + **Returns** + + Marginal distribution list or dict: The reduced mapping if an optional mapping (list or dict) is given + + **Return type** + + dict + + **Raises** + + * **M3Error** – Operator length does not equal bit-string length + * **M3Error** – One or more indices is out of bounds + + diff --git a/docs/api/qiskit-addon-mthree/mthree-utils-stddev.mdx b/docs/api/qiskit-addon-mthree/mthree-utils-stddev.mdx new file mode 100644 index 00000000000..34095ce0aa0 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/mthree-utils-stddev.mdx @@ -0,0 +1,18 @@ +--- +title: stddev (latest version) +description: API reference for mthree.utils.stddev in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: mthree.utils.stddev +--- + + + +# mthree.utils.stddev + + + Compute expectation values from distributions. + + + + diff --git a/docs/api/qiskit-addon-mthree/utils.mdx b/docs/api/qiskit-addon-mthree/utils.mdx new file mode 100644 index 00000000000..504a93b4582 --- /dev/null +++ b/docs/api/qiskit-addon-mthree/utils.mdx @@ -0,0 +1,22 @@ +--- +title: utils (latest version) +description: API reference for mthree.utils in the latest version of qiskit-addon-mthree +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: mthree.utils +--- + + + + + +# Utility functions + +| | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| [`final_measurement_mapping`](/docs/api/qiskit-addon-mthree/mthree-utils-final-measurement-mapping "mthree.utils.final_measurement_mapping")(circuit) | Return the final measurement mapping for the circuit. | +| [`expval`](/docs/api/qiskit-addon-mthree/mthree-utils-expval "mthree.utils.expval")(items\[, exp\_ops]) | Compute expectation values from distributions. | +| [`stddev`](/docs/api/qiskit-addon-mthree/mthree-utils-stddev "mthree.utils.stddev")(items) | Compute expectation values from distributions. | +| [`expval_and_stddev`](/docs/api/qiskit-addon-mthree/mthree-utils-expval-and-stddev "mthree.utils.expval_and_stddev")(items\[, exp\_ops]) | Compute expectation values from distributions. | +| [`marginal_distribution`](/docs/api/qiskit-addon-mthree/mthree-utils-marginal-distribution "mthree.utils.marginal_distribution")(dist, indices\[, mapping]) | Grab the marginal counts from a given distribution. | + 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..27de95afe80 100644 --- a/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx +++ b/docs/api/qiskit-addon-obp/qiskit-addon-obp.mdx @@ -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..2e1fd6536e2 100644 --- a/docs/api/qiskit-addon-obp/release-notes.mdx +++ b/docs/api/qiskit-addon-obp/release-notes.mdx @@ -20,13 +20,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,13 +40,13 @@ 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"). @@ -62,11 +62,11 @@ 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). @@ -80,13 +80,13 @@ in_page_toc_max_heading_level: 2 ### 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`). @@ -94,7 +94,7 @@ in_page_toc_max_heading_level: 2 ### 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 +120,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 +137,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,9 +155,9 @@ 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. @@ -165,9 +165,9 @@ in_page_toc_max_heading_level: 2 ### 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..976e8e57aea 100644 --- a/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx +++ b/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata.mdx @@ -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)) @@ -66,7 +66,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata 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. @@ -111,7 +111,7 @@ python_api_name: qiskit_addon_obp.utils.metadata.OBPMetadata 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. 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..45a07d650e8 100644 --- a/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx +++ b/docs/api/qiskit-addon-obp/utils-metadata-slice-metadata.mdx @@ -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.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-visualization.mdx b/docs/api/qiskit-addon-obp/utils-visualization.mdx index f21a5e38145..cfbaf1bf35c 100644 --- a/docs/api/qiskit-addon-obp/utils-visualization.mdx +++ b/docs/api/qiskit-addon-obp/utils-visualization.mdx @@ -21,7 +21,7 @@ Various visualization utilities. 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. @@ -52,7 +52,7 @@ Various visualization utilities. 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. @@ -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. @@ -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. @@ -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. @@ -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. @@ -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/docs/api/qiskit-addon-opt-mapper/_package.json b/docs/api/qiskit-addon-opt-mapper/_package.json new file mode 100644 index 00000000000..817d860d3fc --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/_package.json @@ -0,0 +1,4 @@ +{ + "name": "qiskit-addon-opt-mapper", + "version": "0.1.0" +} diff --git a/docs/api/qiskit-addon-opt-mapper/_toc.json b/docs/api/qiskit-addon-opt-mapper/_toc.json new file mode 100644 index 00000000000..8ed05b9e4ef --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/_toc.json @@ -0,0 +1,283 @@ +{ + "title": "Optimization mapper", + "children": [ + { + "title": "API index", + "url": "/docs/api/qiskit-addon-opt-mapper" + }, + { + "title": "Release notes", + "useDivider": true, + "url": "/docs/api/qiskit-addon-opt-mapper/release-notes" + }, + { + "title": "qiskit_addon_opt_mapper.applications", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-opt-mapper/applications" + }, + { + "title": "BinPacking", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-bin-packing", + "untranslatable": true + }, + { + "title": "Clique", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-clique", + "untranslatable": true + }, + { + "title": "ExactCover", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-exact-cover", + "untranslatable": true + }, + { + "title": "GraphOptimizationApplication", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-graph-optimization-application", + "untranslatable": true + }, + { + "title": "GraphPartition", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-graph-partition", + "untranslatable": true + }, + { + "title": "Knapsack", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-knapsack", + "untranslatable": true + }, + { + "title": "Maxcut", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-maxcut", + "untranslatable": true + }, + { + "title": "NumberPartition", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-number-partition", + "untranslatable": true + }, + { + "title": "OptimizationApplication", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-optimization-application", + "untranslatable": true + }, + { + "title": "SetPacking", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-set-packing", + "untranslatable": true + }, + { + "title": "SKModel", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-sk-model", + "untranslatable": true + }, + { + "title": "Tsp", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-tsp", + "untranslatable": true + }, + { + "title": "VehicleRouting", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-vehicle-routing", + "untranslatable": true + }, + { + "title": "VertexCover", + "url": "/docs/api/qiskit-addon-opt-mapper/applications-vertex-cover", + "untranslatable": true + } + ] + }, + { + "title": "qiskit_addon_opt_mapper.converters", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-opt-mapper/converters" + }, + { + "title": "BinaryToLinearBinary", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-binary-to-linear-binary", + "untranslatable": true + }, + { + "title": "BinaryToSpin", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-binary-to-spin", + "untranslatable": true + }, + { + "title": "InequalityToEquality", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-inequality-to-equality", + "untranslatable": true + }, + { + "title": "IntegerToBinary", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-integer-to-binary", + "untranslatable": true + }, + { + "title": "LinearInequalityToPenalty", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-linear-inequality-to-penalty", + "untranslatable": true + }, + { + "title": "MaximizeToMinimize", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-maximize-to-minimize", + "untranslatable": true + }, + { + "title": "MinimizeToMaximize", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-minimize-to-maximize", + "untranslatable": true + }, + { + "title": "OptimizationProblemConverter", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-converter", + "untranslatable": true + }, + { + "title": "OptimizationProblemToHubo", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-hubo", + "untranslatable": true + }, + { + "title": "OptimizationProblemToQubo", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-qubo", + "untranslatable": true + }, + { + "title": "SpinToBinary", + "url": "/docs/api/qiskit-addon-opt-mapper/converters-spin-to-binary", + "untranslatable": true + } + ] + }, + { + "title": "qiskit_addon_opt_mapper.problems", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-opt-mapper/problems" + }, + { + "title": "Constraint", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-constraint", + "untranslatable": true + }, + { + "title": "HigherOrderConstraint", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-higher-order-constraint", + "untranslatable": true + }, + { + "title": "HigherOrderExpression", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression", + "untranslatable": true + }, + { + "title": "LinearConstraint", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-linear-constraint", + "untranslatable": true + }, + { + "title": "LinearExpression", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-linear-expression", + "untranslatable": true + }, + { + "title": "OptimizationObjective", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-optimization-objective", + "untranslatable": true + }, + { + "title": "OptimizationProblemElement", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-optimization-problem-element", + "untranslatable": true + }, + { + "title": "QuadraticConstraint", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-quadratic-constraint", + "untranslatable": true + }, + { + "title": "QuadraticExpression", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-quadratic-expression", + "untranslatable": true + }, + { + "title": "Variable", + "url": "/docs/api/qiskit-addon-opt-mapper/problems-variable", + "untranslatable": true + } + ] + }, + { + "title": "qiskit_addon_opt_mapper.solvers", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers" + }, + { + "title": "CplexSolver", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers-cplex-solver", + "untranslatable": true + }, + { + "title": "GurobiSolver", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers-gurobi-solver", + "untranslatable": true + }, + { + "title": "OptimizationSolver", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver", + "untranslatable": true + }, + { + "title": "ScipyMilpSolver", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers-scipy-milp-solver", + "untranslatable": true + }, + { + "title": "SolverResult", + "url": "/docs/api/qiskit-addon-opt-mapper/solvers-solver-result", + "untranslatable": true + } + ] + }, + { + "title": "qiskit_addon_opt_mapper.translators", + "untranslatable": true, + "children": [ + { + "title": "Module overview", + "url": "/docs/api/qiskit-addon-opt-mapper/translators" + }, + { + "title": "from_docplex_mp", + "url": "/docs/api/qiskit-addon-opt-mapper/translators-from-docplex-mp", + "untranslatable": true + }, + { + "title": "to_docplex_mp", + "url": "/docs/api/qiskit-addon-opt-mapper/translators-to-docplex-mp", + "untranslatable": true + }, + { + "title": "to_ising", + "url": "/docs/api/qiskit-addon-opt-mapper/translators-to-ising", + "untranslatable": true + } + ] + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-opt-mapper", + "parentLabel": "Optimization mapper" +} diff --git a/docs/api/qiskit-addon-opt-mapper/applications-bin-packing.mdx b/docs/api/qiskit-addon-opt-mapper/applications-bin-packing.mdx new file mode 100644 index 00000000000..510f499d995 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-bin-packing.mdx @@ -0,0 +1,130 @@ +--- +title: BinPacking (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.BinPacking in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.BinPacking +--- + + + +# qiskit\_addon\_opt\_mapper.applications.BinPacking + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application for the “bin packing” \[1] problem. + + **References** + + \[1]: “Bin packing”, [https://en.wikipedia.org/wiki/Bin\_packing\_problem](https://en.wikipedia.org/wiki/Bin_packing_problem) + + BinPacking init. + + **Parameters** + + * **weights** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the weights of items + * **max\_weight** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum bin weight capacity + * **max\_number\_of\_bins** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – The maximum number of bins by default equal to the number of items + + ### \_\_init\_\_ + + + BinPacking init. + + **Parameters** + + * **weights** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the weights of items + * **max\_weight** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum bin weight capacity + * **max\_number\_of\_bins** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – The maximum number of bins by default equal to the number of items + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.BinPacking.__init__ "qiskit_addon_opt_mapper.applications.BinPacking.__init__")(weights, max\_weight\[, ...]) | BinPacking init. | + | [`get_figure`](#qiskit_addon_opt_mapper.applications.BinPacking.get_figure "qiskit_addon_opt_mapper.applications.BinPacking.get_figure")(result) | Get plot of the solution of the Bin Packing Problem. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.BinPacking.interpret "qiskit_addon_opt_mapper.applications.BinPacking.interpret")(result) | Interpret a result as item indices. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.BinPacking.sample_most_likely "qiskit_addon_opt_mapper.applications.BinPacking.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.BinPacking.to_optimization_problem "qiskit_addon_opt_mapper.applications.BinPacking.to_optimization_problem")() | Represent as an optimization problem. | + + ### get\_figure + + + Get plot of the solution of the Bin Packing Problem. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A plot of the solution, where x and y represent the bins and sum of the weights respectively. + + **Return type** + + fig + + + ### interpret + + + Interpret a result as item indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of lists with the items in each bin + + **Return type** + + items\_in\_bins + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a bin packing problem instance into an `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the bin packing problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-clique.mdx b/docs/api/qiskit-addon-opt-mapper/applications-clique.mdx new file mode 100644 index 00000000000..88085498e8c --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-clique.mdx @@ -0,0 +1,168 @@ +--- +title: Clique (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.Clique in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.Clique +--- + + + +# qiskit\_addon\_opt\_mapper.applications.Clique + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “clique” \[1] problem based on a NetworkX graph. + + **References** + + \[1]: “Clique (graph theory)”, [https://en.wikipedia.org/wiki/Clique\_(graph\_theory)](https://en.wikipedia.org/wiki/Clique_\(graph_theory\)) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)) – A graph representing a problem. It can be specified directly as a [NetworkX](https://networkx.org/) graph, or as an array or list format suitable to build out a NetworkX graph. + * **size** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – The size of the clique. When it’s None, the default, this class makes an optimization model for a maximal clique instead of the specified size of a clique. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)) – + + A graph representing a problem. It can be specified directly as a [NetworkX](https://networkx.org/) graph, or as an array or list format suitable to build out a NetworkX graph. + + * **size** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – The size of the clique. When it’s None, the default, this class makes an optimization model for a maximal clique instead of the specified size of a clique. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.Clique.__init__ "qiskit_addon_opt_mapper.applications.Clique.__init__")(graph\[, size]) | Init method. | + | [`draw`](#qiskit_addon_opt_mapper.applications.Clique.draw "qiskit_addon_opt_mapper.applications.Clique.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.Clique.interpret "qiskit_addon_opt_mapper.applications.Clique.interpret")(result) | Interpret a result as a list of node indices. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.Clique.sample_most_likely "qiskit_addon_opt_mapper.applications.Clique.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.Clique.to_optimization_problem "qiskit_addon_opt_mapper.applications.Clique.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.Clique.graph "qiskit_addon_opt_mapper.applications.Clique.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.Clique.nx_graph "qiskit_addon_opt_mapper.applications.Clique.nx_graph") | Getter of the graph in Networkx format. | + | [`size`](#qiskit_addon_opt_mapper.applications.Clique.size "qiskit_addon_opt_mapper.applications.Clique.size") | Getter of size. | + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as a list of node indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + The list of node indices whose corresponding variable is 1 + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)] + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### size + + + Getter of size. + + **Returns** + + The size of the clique, None when maximal clique + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a clique problem instance into a `OptimizationProblem`. When “size” is None, this makes an optimization model for a maximal clique instead of the specified size of a clique. + + **Returns** + + The `OptimizationProblem` created from the clique problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-exact-cover.mdx b/docs/api/qiskit-addon-opt-mapper/applications-exact-cover.mdx new file mode 100644 index 00000000000..75548ce3e7b --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-exact-cover.mdx @@ -0,0 +1,107 @@ +--- +title: ExactCover (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.ExactCover in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.ExactCover +--- + + + +# qiskit\_addon\_opt\_mapper.applications.ExactCover + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application for the “exact cover” \[1] problem. + + **References** + + \[1]: “Exact cover”, [https://en.wikipedia.org/wiki/Exact\_cover](https://en.wikipedia.org/wiki/Exact_cover) + + Init method. + + **Parameters** + + **subsets** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]]*) – A list of subsets + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **subsets** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]]*) – A list of subsets + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.ExactCover.__init__ "qiskit_addon_opt_mapper.applications.ExactCover.__init__")(subsets) | Init method. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.ExactCover.interpret "qiskit_addon_opt_mapper.applications.ExactCover.interpret")(result) | Interpret a result as a list of subsets. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.ExactCover.sample_most_likely "qiskit_addon_opt_mapper.applications.ExactCover.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.ExactCover.to_optimization_problem "qiskit_addon_opt_mapper.applications.ExactCover.to_optimization_problem")() | Represent as an optimization problem. | + + ### interpret + + + Interpret a result as a list of subsets. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of subsets whose corresponding variable is 1 + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert an exact cover instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the exact cover instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-graph-optimization-application.mdx b/docs/api/qiskit-addon-opt-mapper/applications-graph-optimization-application.mdx new file mode 100644 index 00000000000..e86ba32b4db --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-graph-optimization-application.mdx @@ -0,0 +1,156 @@ +--- +title: GraphOptimizationApplication (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.GraphOptimizationApplication in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.GraphOptimizationApplication +--- + + + +# qiskit\_addon\_opt\_mapper.applications.GraphOptimizationApplication + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + An abstract class for graph optimization applications. + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.__init__ "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.__init__")(graph) | Init method. | + | [`draw`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.draw "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.interpret "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.interpret")(result) | Interpret the problem. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.sample_most_likely "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.to_optimization_problem "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.graph "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.nx_graph "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication.nx_graph") | Getter of the graph in Networkx format. | + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret the problem. + + Convert the calculation result of the problem ([`SolverResult`](solvers-solver-result "qiskit_addon_opt_mapper.solvers.SolverResult") or a binary array using np.ndarray) to the answer of the problem in an easy-to-understand format. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a problem instance into a `OptimizationProblem` + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-graph-partition.mdx b/docs/api/qiskit-addon-opt-mapper/applications-graph-partition.mdx new file mode 100644 index 00000000000..eed32418ca1 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-graph-partition.mdx @@ -0,0 +1,170 @@ +--- +title: GraphPartition (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.GraphPartition in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.GraphPartition +--- + + + +# qiskit\_addon\_opt\_mapper.applications.GraphPartition + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “graph partition” \[1] problem based on a NetworkX graph. + + **References** + + \[1]: “Graph partition”, [https://en.wikipedia.org/wiki/Graph\_partition](https://en.wikipedia.org/wiki/Graph_partition) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.GraphPartition.__init__ "qiskit_addon_opt_mapper.applications.GraphPartition.__init__")(graph) | Init method. | + | [`draw`](#qiskit_addon_opt_mapper.applications.GraphPartition.draw "qiskit_addon_opt_mapper.applications.GraphPartition.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.GraphPartition.interpret "qiskit_addon_opt_mapper.applications.GraphPartition.interpret")(result) | Interpret a result as a list of node indices. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.GraphPartition.sample_most_likely "qiskit_addon_opt_mapper.applications.GraphPartition.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.GraphPartition.to_optimization_problem "qiskit_addon_opt_mapper.applications.GraphPartition.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.GraphPartition.graph "qiskit_addon_opt_mapper.applications.GraphPartition.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.GraphPartition.nx_graph "qiskit_addon_opt_mapper.applications.GraphPartition.nx_graph") | Getter of the graph in Networkx format. | + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as a list of node indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of node indices divided into two groups. + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a graph partition instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the graph partition instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-knapsack.mdx b/docs/api/qiskit-addon-opt-mapper/applications-knapsack.mdx new file mode 100644 index 00000000000..2635443c3ea --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-knapsack.mdx @@ -0,0 +1,127 @@ +--- +title: Knapsack (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.Knapsack in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.Knapsack +--- + + + +# qiskit\_addon\_opt\_mapper.applications.Knapsack + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application for the “knapsack problem” \[1]. + + **References** + + \[1]: “Knapsack problem”, [https://en.wikipedia.org/wiki/Knapsack\_problem](https://en.wikipedia.org/wiki/Knapsack_problem) + + Init method. + + **Parameters** + + * **values** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the values of items + * **weights** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the weights of items + * **max\_weight** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum weight capacity + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **values** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the values of items + * **weights** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of the weights of items + * **max\_weight** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum weight capacity + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.Knapsack.__init__ "qiskit_addon_opt_mapper.applications.Knapsack.__init__")(values, weights, max\_weight) | Init method. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.Knapsack.interpret "qiskit_addon_opt_mapper.applications.Knapsack.interpret")(result) | Interpret a result as item indices. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.Knapsack.sample_most_likely "qiskit_addon_opt_mapper.applications.Knapsack.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.Knapsack.to_optimization_problem "qiskit_addon_opt_mapper.applications.Knapsack.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | + | [`max_weight`](#qiskit_addon_opt_mapper.applications.Knapsack.max_weight "qiskit_addon_opt_mapper.applications.Knapsack.max_weight") | Getter of max\_weight. | + + ### interpret + + + Interpret a result as item indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of items whose corresponding variable is 1 + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)] + + + ### max\_weight + + + Getter of max\_weight. + + **Returns** + + The maximal weight for the knapsack problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a knapsack problem instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the knapsack problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-maxcut.mdx b/docs/api/qiskit-addon-opt-mapper/applications-maxcut.mdx new file mode 100644 index 00000000000..7ba8de19f6c --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-maxcut.mdx @@ -0,0 +1,208 @@ +--- +title: Maxcut (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.Maxcut in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.Maxcut +--- + + + +# qiskit\_addon\_opt\_mapper.applications.Maxcut + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “max-cut” \[1] problem based on a NetworkX graph. + + **References** + + \[1]: “Maximum cut”, [https://en.wikipedia.org/wiki/Maximum\_cut](https://en.wikipedia.org/wiki/Maximum_cut) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.Maxcut.__init__ "qiskit_addon_opt_mapper.applications.Maxcut.__init__")(graph) | Init method. | + | [`draw`](#qiskit_addon_opt_mapper.applications.Maxcut.draw "qiskit_addon_opt_mapper.applications.Maxcut.draw")(\[result, pos]) | Draw a graph with the result. | + | [`get_gset_result`](#qiskit_addon_opt_mapper.applications.Maxcut.get_gset_result "qiskit_addon_opt_mapper.applications.Maxcut.get_gset_result")(x) | Get graph solution in Gset format from binary string. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.Maxcut.interpret "qiskit_addon_opt_mapper.applications.Maxcut.interpret")(result) | Interpret a result as two lists of node indices. | + | [`parse_gset_format`](#qiskit_addon_opt_mapper.applications.Maxcut.parse_gset_format "qiskit_addon_opt_mapper.applications.Maxcut.parse_gset_format")(filename) | Read graph in Gset format from file. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.Maxcut.sample_most_likely "qiskit_addon_opt_mapper.applications.Maxcut.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.Maxcut.to_optimization_problem "qiskit_addon_opt_mapper.applications.Maxcut.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.Maxcut.graph "qiskit_addon_opt_mapper.applications.Maxcut.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.Maxcut.nx_graph "qiskit_addon_opt_mapper.applications.Maxcut.nx_graph") | Getter of the graph in Networkx format. | + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### get\_gset\_result + + + Get graph solution in Gset format from binary string. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – binary string as numpy array. + + **Returns** + + A graph solution in Gset format. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[int](https://docs.python.org/3/library/functions.html#int), [int](https://docs.python.org/3/library/functions.html#int)] + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as two lists of node indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + Two lists of node indices correspond to two node sets for the Max-cut + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### parse\_gset\_format + + + Read graph in Gset format from file. + + **Parameters** + + **filename** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – the name of the file. + + **Returns** + + An adjacency matrix as a 2D numpy array. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a Max-cut problem instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the Max-cut problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-number-partition.mdx b/docs/api/qiskit-addon-opt-mapper/applications-number-partition.mdx new file mode 100644 index 00000000000..341e2b15c12 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-number-partition.mdx @@ -0,0 +1,107 @@ +--- +title: NumberPartition (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.NumberPartition in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.NumberPartition +--- + + + +# qiskit\_addon\_opt\_mapper.applications.NumberPartition + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application for the “number partition” \[1] problem. + + **References** + + \[1]: “Partition problem”, [https://en.wikipedia.org/wiki/Partition\_problem](https://en.wikipedia.org/wiki/Partition_problem) + + Init method. + + **Parameters** + + **number\_set** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of integers + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **number\_set** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – A list of integers + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.NumberPartition.__init__ "qiskit_addon_opt_mapper.applications.NumberPartition.__init__")(number\_set) | Init method. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.NumberPartition.interpret "qiskit_addon_opt_mapper.applications.NumberPartition.interpret")(result) | Interpret a result as a list of subsets. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.NumberPartition.sample_most_likely "qiskit_addon_opt_mapper.applications.NumberPartition.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.NumberPartition.to_optimization_problem "qiskit_addon_opt_mapper.applications.NumberPartition.to_optimization_problem")() | Represent as an optimization problem. | + + ### interpret + + + Interpret a result as a list of subsets. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of subsets whose sum is the half of the total. + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a number partitioning problem instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the number partitioning problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-optimization-application.mdx b/docs/api/qiskit-addon-opt-mapper/applications-optimization-application.mdx new file mode 100644 index 00000000000..a1cb690c70d --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-optimization-application.mdx @@ -0,0 +1,77 @@ +--- +title: OptimizationApplication (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.OptimizationApplication in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.OptimizationApplication +--- + + + +# qiskit\_addon\_opt\_mapper.applications.OptimizationApplication + + + Bases: [`ABC`](https://docs.python.org/3/library/abc.html#abc.ABC) + + An abstract class for optimization applications. + + ### \_\_init\_\_ + + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.OptimizationApplication.__init__ "qiskit_addon_opt_mapper.applications.OptimizationApplication.__init__")() | | + | [`interpret`](#qiskit_addon_opt_mapper.applications.OptimizationApplication.interpret "qiskit_addon_opt_mapper.applications.OptimizationApplication.interpret")(result) | Interpret the problem. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.OptimizationApplication.sample_most_likely "qiskit_addon_opt_mapper.applications.OptimizationApplication.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.OptimizationApplication.to_optimization_problem "qiskit_addon_opt_mapper.applications.OptimizationApplication.to_optimization_problem")() | Represent as an optimization problem. | + + ### interpret + + + Interpret the problem. + + Convert the calculation result of the problem ([`SolverResult`](solvers-solver-result "qiskit_addon_opt_mapper.solvers.SolverResult") or a binary array using np.ndarray) to the answer of the problem in an easy-to-understand format. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a problem instance into a `OptimizationProblem` + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-set-packing.mdx b/docs/api/qiskit-addon-opt-mapper/applications-set-packing.mdx new file mode 100644 index 00000000000..33f7183a4fa --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-set-packing.mdx @@ -0,0 +1,107 @@ +--- +title: SetPacking (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.SetPacking in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.SetPacking +--- + + + +# qiskit\_addon\_opt\_mapper.applications.SetPacking + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application for the “set packing” \[1] problem. + + **References** + + \[1]: “Set packing”, [https://en.wikipedia.org/wiki/Set\_packing](https://en.wikipedia.org/wiki/Set_packing) + + Init method. + + **Parameters** + + **subsets** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]]*) – A list of subsets + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **subsets** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]]*) – A list of subsets + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.SetPacking.__init__ "qiskit_addon_opt_mapper.applications.SetPacking.__init__")(subsets) | Init method. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.SetPacking.interpret "qiskit_addon_opt_mapper.applications.SetPacking.interpret")(result) | Interpret a result as a list of subsets. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.SetPacking.sample_most_likely "qiskit_addon_opt_mapper.applications.SetPacking.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.SetPacking.to_optimization_problem "qiskit_addon_opt_mapper.applications.SetPacking.to_optimization_problem")() | Represent as an optimization problem. | + + ### interpret + + + Interpret a result as a list of subsets. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of subsets whose corresponding variable is 1 + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a set packing instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the set packing instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-sk-model.mdx b/docs/api/qiskit-addon-opt-mapper/applications-sk-model.mdx new file mode 100644 index 00000000000..34a6fc3f562 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-sk-model.mdx @@ -0,0 +1,156 @@ +--- +title: SKModel (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.SKModel in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.SKModel +--- + + + +# qiskit\_addon\_opt\_mapper.applications.SKModel + + + Bases: [`OptimizationApplication`](applications-optimization-application "qiskit_addon_opt_mapper.applications.optimization_application.OptimizationApplication") + + Optimization application of the “Sherrington Kirkpatrick (SK) model” \[1]. + + The SK Hamiltonian over n spins is given as: $H(x)=-1/\sqrt{n} \sum_{i) or None. None results in usage of np.random.default\_rng(). + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **num\_sites** ([*int*](https://docs.python.org/3/library/functions.html#int)) – number of sites + * **rng\_or\_seed** ([*Generator*](https://numpy.org/doc/stable/reference/random/generator.html#numpy.random.Generator) *|*[*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – NumPy pseudo-random number generator or seed for np.random.default\_rng(\) or None. None results in usage of np.random.default\_rng(). + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.SKModel.__init__ "qiskit_addon_opt_mapper.applications.SKModel.__init__")(num\_sites\[, rng\_or\_seed]) | Init method. | + | [`disorder`](#qiskit_addon_opt_mapper.applications.SKModel.disorder "qiskit_addon_opt_mapper.applications.SKModel.disorder")() | Generate a new disorder of the SK model. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.SKModel.interpret "qiskit_addon_opt_mapper.applications.SKModel.interpret")(result) | Interpret a result as configuration of spins. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.SKModel.sample_most_likely "qiskit_addon_opt_mapper.applications.SKModel.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.SKModel.to_optimization_problem "qiskit_addon_opt_mapper.applications.SKModel.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.SKModel.graph "qiskit_addon_opt_mapper.applications.SKModel.graph") | Getter of the graph representation. | + | [`num_sites`](#qiskit_addon_opt_mapper.applications.SKModel.num_sites "qiskit_addon_opt_mapper.applications.SKModel.num_sites") | Getter of the number of sites. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.SKModel.nx_graph "qiskit_addon_opt_mapper.applications.SKModel.nx_graph") | Getter of the graph in Networkx format. | + + ### disorder + + + Generate a new disorder of the SK model. + + **Return type** + + None + + + ### graph + + + Getter of the graph representation. + + **Returns** + + A graph for a problem. + + + ### interpret + + + Interpret a result as configuration of spins. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem. + + **Returns** + + configuration of spins + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)] + + + ### num\_sites + + + Getter of the number of sites. + + **Returns** + + Number of sites. + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert an SK model problem instance into a `OptimizationProblem`. + + **Returns** + + The `OptimizationProblem` created from the SK problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-tsp.mdx b/docs/api/qiskit-addon-opt-mapper/applications-tsp.mdx new file mode 100644 index 00000000000..f7f5707267f --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-tsp.mdx @@ -0,0 +1,238 @@ +--- +title: Tsp (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.Tsp in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.Tsp +--- + + + +# qiskit\_addon\_opt\_mapper.applications.Tsp + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “traveling salesman problem” \[1] based on a NetworkX graph. + + **References** + + \[1]: “Travelling salesman problem”, [https://en.wikipedia.org/wiki/Travelling\_salesman\_problem](https://en.wikipedia.org/wiki/Travelling_salesman_problem) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | + | [`__init__`](#qiskit_addon_opt_mapper.applications.Tsp.__init__ "qiskit_addon_opt_mapper.applications.Tsp.__init__")(graph) | Init method. | + | [`create_random_instance`](#qiskit_addon_opt_mapper.applications.Tsp.create_random_instance "qiskit_addon_opt_mapper.applications.Tsp.create_random_instance")(n\[, low, high, seed]) | Create a random instance of the traveling salesman problem. | + | [`draw`](#qiskit_addon_opt_mapper.applications.Tsp.draw "qiskit_addon_opt_mapper.applications.Tsp.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.Tsp.interpret "qiskit_addon_opt_mapper.applications.Tsp.interpret")(result) | Interpret a result as a list of node indices. | + | [`parse_tsplib_format`](#qiskit_addon_opt_mapper.applications.Tsp.parse_tsplib_format "qiskit_addon_opt_mapper.applications.Tsp.parse_tsplib_format")(filename) | Read a graph in TSPLIB format from file and return a Tsp instance. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.Tsp.sample_most_likely "qiskit_addon_opt_mapper.applications.Tsp.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.Tsp.to_optimization_problem "qiskit_addon_opt_mapper.applications.Tsp.to_optimization_problem")() | Represent as an optimization problem. | + | [`tsp_value`](#qiskit_addon_opt_mapper.applications.Tsp.tsp_value "qiskit_addon_opt_mapper.applications.Tsp.tsp_value")(z, adj\_matrix) | Compute the TSP value of a solution. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.Tsp.graph "qiskit_addon_opt_mapper.applications.Tsp.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.Tsp.nx_graph "qiskit_addon_opt_mapper.applications.Tsp.nx_graph") | Getter of the graph in Networkx format. | + + ### create\_random\_instance + + + Create a random instance of the traveling salesman problem. + + **Parameters** + + * **n** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the number of nodes. + * **low** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The minimum value for the coordinate of a node. + * **high** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum value for the coordinate of a node. + * **seed** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – the seed for the random coordinates. + + **Returns** + + A Tsp instance created from the input information + + **Return type** + + [*Tsp*](#qiskit_addon_opt_mapper.applications.Tsp "qiskit_addon_opt_mapper.applications.tsp.Tsp") + + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as a list of node indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of nodes whose indices correspond to its order in a prospective cycle. + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int) | [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### parse\_tsplib\_format + + + Read a graph in TSPLIB format from file and return a Tsp instance. + + Only the EUC\_2D edge weight format is supported. + + **Parameters** + + **filename** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – the name of the file. + + **Raises** + + * **OptimizationError** – If the type is not “TSP” + * **OptimizationError** – If the edge weight type is not “EUC\_2D” + + **Returns** + + A Tsp instance data. + + **Return type** + + [*Tsp*](#qiskit_addon_opt_mapper.applications.Tsp "qiskit_addon_opt_mapper.applications.tsp.Tsp") + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a traveling salesman problem instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the traveling salesman problem instance. + + **Return type** + + *OptimizationProblem* + + + ### tsp\_value + + + Compute the TSP value of a solution. + + **Parameters** + + * **z** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – list of cities. + * **adj\_matrix** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – adjacency matrix. + + **Returns** + + value of the total length + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-vehicle-routing.mdx b/docs/api/qiskit-addon-opt-mapper/applications-vehicle-routing.mdx new file mode 100644 index 00000000000..9b4e799d145 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-vehicle-routing.mdx @@ -0,0 +1,206 @@ +--- +title: VehicleRouting (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.VehicleRouting in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.VehicleRouting +--- + + + +# qiskit\_addon\_opt\_mapper.applications.VehicleRouting + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “vehicle routing problem” \[1] based on a `NetworkX` graph. + + **References** + + \[1]: “Vehicle routing problem”, [https://en.wikipedia.org/wiki/Vehicle\_routing\_problem](https://en.wikipedia.org/wiki/Vehicle_routing_problem) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)) – A graph representing a problem. It can be specified directly as a [NetworkX](https://networkx.org/) graph, or as an array or list format suitable to build out a NetworkX graph. + * **num\_vehicles** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of vehicles + * **depot** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The index of the depot node where all the vehicle depart + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)) – + + A graph representing a problem. It can be specified directly as a [NetworkX](https://networkx.org/) graph, or as an array or list format suitable to build out a NetworkX graph. + + * **num\_vehicles** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of vehicles + + * **depot** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The index of the depot node where all the vehicle depart + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | + | [`__init__`](#qiskit_addon_opt_mapper.applications.VehicleRouting.__init__ "qiskit_addon_opt_mapper.applications.VehicleRouting.__init__")(graph\[, num\_vehicles, depot]) | Init method. | + | [`create_random_instance`](#qiskit_addon_opt_mapper.applications.VehicleRouting.create_random_instance "qiskit_addon_opt_mapper.applications.VehicleRouting.create_random_instance")(n\[, low, high, seed, ...]) | Create a random instance of the vehicle routing problem. | + | [`draw`](#qiskit_addon_opt_mapper.applications.VehicleRouting.draw "qiskit_addon_opt_mapper.applications.VehicleRouting.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.VehicleRouting.interpret "qiskit_addon_opt_mapper.applications.VehicleRouting.interpret")(result) | Interpret a result as a list of the routes for each vehicle. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.VehicleRouting.sample_most_likely "qiskit_addon_opt_mapper.applications.VehicleRouting.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.VehicleRouting.to_optimization_problem "qiskit_addon_opt_mapper.applications.VehicleRouting.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- | + | [`depot`](#qiskit_addon_opt_mapper.applications.VehicleRouting.depot "qiskit_addon_opt_mapper.applications.VehicleRouting.depot") | Getter of depot. | + | [`graph`](#qiskit_addon_opt_mapper.applications.VehicleRouting.graph "qiskit_addon_opt_mapper.applications.VehicleRouting.graph") | Getter of the graph. | + | [`num_vehicles`](#qiskit_addon_opt_mapper.applications.VehicleRouting.num_vehicles "qiskit_addon_opt_mapper.applications.VehicleRouting.num_vehicles") | Getter of num\_vehicles. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.VehicleRouting.nx_graph "qiskit_addon_opt_mapper.applications.VehicleRouting.nx_graph") | Getter of the graph in Networkx format. | + + ### create\_random\_instance + + + Create a random instance of the vehicle routing problem. + + **Parameters** + + * **n** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the number of nodes. + * **low** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The minimum value for the coordinate of a node. + * **high** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum value for the coordinate of a node. + * **seed** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – the seed for the random coordinates. + * **num\_vehicle** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of the vehicles + * **depot** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The index of the depot node where all the vehicle depart + + **Returns** + + A VehicleRouting instance created from the input information + + **Return type** + + [*VehicleRouting*](#qiskit_addon_opt_mapper.applications.VehicleRouting "qiskit_addon_opt_mapper.applications.vehicle_routing.VehicleRouting") + + + ### depot + + + Getter of depot. + + **Returns** + + The node index of the depot where all the vehicles depart + + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as a list of the routes for each vehicle. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of the routes for each vehicle + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]]] + + + ### num\_vehicles + + + Getter of num\_vehicles. + + **Returns** + + The number of the vehicles + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a vehicle routing problem instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the vehicle routing problem instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications-vertex-cover.mdx b/docs/api/qiskit-addon-opt-mapper/applications-vertex-cover.mdx new file mode 100644 index 00000000000..32075ba65e7 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications-vertex-cover.mdx @@ -0,0 +1,170 @@ +--- +title: VertexCover (latest version) +description: API reference for qiskit_addon_opt_mapper.applications.VertexCover in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.applications.VertexCover +--- + + + +# qiskit\_addon\_opt\_mapper.applications.VertexCover + + + Bases: [`GraphOptimizationApplication`](applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.graph_optimization_application.GraphOptimizationApplication") + + Optimization application for the “vertex cover” \[1] problem based on a NetworkX graph. + + **References** + + \[1]: “Vertex cover”, [https://en.wikipedia.org/wiki/Vertex\_cover](https://en.wikipedia.org/wiki/Vertex_cover) + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **graph** (*Graph |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*PyGraph*](https://www.rustworkx.org/apiref/rustworkx.PyGraph.html#rustworkx.PyGraph)) – + + A graph representing a problem. It can be specified in the following formats: + + * A Rustworkx undirected graph (`rx.PyGraph`) + * A NetworkX undirected graph (`nx.Graph`) + * A NumPy adjacency matrix (`np.ndarray`) + * A list of edges or adjacency list (`list`) + + * **rx.PyGraph.** (*The input graph will be internally normalized to a*) + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.applications.VertexCover.__init__ "qiskit_addon_opt_mapper.applications.VertexCover.__init__")(graph) | Init method. | + | [`draw`](#qiskit_addon_opt_mapper.applications.VertexCover.draw "qiskit_addon_opt_mapper.applications.VertexCover.draw")(\[result, pos]) | Draw a graph with the result. | + | [`interpret`](#qiskit_addon_opt_mapper.applications.VertexCover.interpret "qiskit_addon_opt_mapper.applications.VertexCover.interpret")(result) | Interpret a result as a list of node indices. | + | [`sample_most_likely`](#qiskit_addon_opt_mapper.applications.VertexCover.sample_most_likely "qiskit_addon_opt_mapper.applications.VertexCover.sample_most_likely")(state\_vector) | Compute the most likely binary string from state vector. | + | [`to_optimization_problem`](#qiskit_addon_opt_mapper.applications.VertexCover.to_optimization_problem "qiskit_addon_opt_mapper.applications.VertexCover.to_optimization_problem")() | Represent as an optimization problem. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- | + | [`graph`](#qiskit_addon_opt_mapper.applications.VertexCover.graph "qiskit_addon_opt_mapper.applications.VertexCover.graph") | Getter of the graph. | + | [`nx_graph`](#qiskit_addon_opt_mapper.applications.VertexCover.nx_graph "qiskit_addon_opt_mapper.applications.VertexCover.nx_graph") | Getter of the graph in Networkx format. | + + ### draw + + + Draw a graph with the result. + + When the result is None, draw an original graph without colors. + + **Parameters** + + * **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – The calculated result for the problem + * **pos** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)*] | None*) – The positions of nodes + + **Return type** + + None + + + ### graph + + + Getter of the graph. + + **Returns** + + A graph for a problem + + + ### interpret + + + Interpret a result as a list of node indices. + + **Parameters** + + **result** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – The calculated result of the problem + + **Returns** + + A list of node indices whose corresponding variable is 1 + + **Return type** + + [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)] + + + ### nx\_graph + + + Getter of the graph in Networkx format. + + **Returns** + + A graph for a problem + + + ### sample\_most\_likely + + + Compute the most likely binary string from state vector. + + **Parameters** + + **state\_vector** ([*QuasiDistribution*](/docs/api/qiskit/qiskit.result.QuasiDistribution) *|*[*Statevector*](/docs/api/qiskit/qiskit.quantum_info.Statevector) *|*[*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)) – state vector or counts or quasi-probabilities. + + **Returns** + + binary string as numpy.ndarray of ints. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if state\_vector is not QuasiDistribution, Statevector, np.ndarray, or dict. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_optimization\_problem + + + Represent as an optimization problem. + + Convert a vertex cover instance into a `OptimizationProblem` + + **Returns** + + The `OptimizationProblem` created from the vertex cover instance. + + **Return type** + + *OptimizationProblem* + + + diff --git a/docs/api/qiskit-addon-opt-mapper/applications.mdx b/docs/api/qiskit-addon-opt-mapper/applications.mdx new file mode 100644 index 00000000000..8c4519e571e --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/applications.mdx @@ -0,0 +1,50 @@ +--- +title: applications (latest version) +description: API reference for qiskit_addon_opt_mapper.applications in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_opt_mapper.applications +--- + + + + + +# Optimization Application Modeling + +`qiskit_addon_opt_mapper.applications` + +Optimization applications. + + + +## Optimization applications . + +`qiskit_addon_opt_mapper.applications` + +Applications for common optimization problems. + +### Base classes for applications + +| | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| [`OptimizationApplication`](/docs/api/qiskit-addon-opt-mapper/applications-optimization-application "qiskit_addon_opt_mapper.applications.OptimizationApplication") | An abstract class for optimization applications. | +| [`GraphOptimizationApplication`](/docs/api/qiskit-addon-opt-mapper/applications-graph-optimization-application "qiskit_addon_opt_mapper.applications.GraphOptimizationApplication") | An abstract class for graph optimization applications. | + +### Applications + +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| [`BinPacking`](/docs/api/qiskit-addon-opt-mapper/applications-bin-packing "qiskit_addon_opt_mapper.applications.BinPacking") | Optimization application for the "bin packing" \[1] problem. | +| [`Clique`](/docs/api/qiskit-addon-opt-mapper/applications-clique "qiskit_addon_opt_mapper.applications.Clique") | Optimization application for the "clique" \[1] problem based on a NetworkX graph. | +| [`ExactCover`](/docs/api/qiskit-addon-opt-mapper/applications-exact-cover "qiskit_addon_opt_mapper.applications.ExactCover") | Optimization application for the "exact cover" \[1] problem. | +| [`GraphPartition`](/docs/api/qiskit-addon-opt-mapper/applications-graph-partition "qiskit_addon_opt_mapper.applications.GraphPartition") | Optimization application for the "graph partition" \[1] problem based on a NetworkX graph. | +| [`Knapsack`](/docs/api/qiskit-addon-opt-mapper/applications-knapsack "qiskit_addon_opt_mapper.applications.Knapsack") | Optimization application for the "knapsack problem" \[1]. | +| [`Maxcut`](/docs/api/qiskit-addon-opt-mapper/applications-maxcut "qiskit_addon_opt_mapper.applications.Maxcut") | Optimization application for the "max-cut" \[1] problem based on a NetworkX graph. | +| [`NumberPartition`](/docs/api/qiskit-addon-opt-mapper/applications-number-partition "qiskit_addon_opt_mapper.applications.NumberPartition") | Optimization application for the "number partition" \[1] problem. | +| [`SetPacking`](/docs/api/qiskit-addon-opt-mapper/applications-set-packing "qiskit_addon_opt_mapper.applications.SetPacking") | Optimization application for the "set packing" \[1] problem. | +| [`SKModel`](/docs/api/qiskit-addon-opt-mapper/applications-sk-model "qiskit_addon_opt_mapper.applications.SKModel") | Optimization application of the "Sherrington Kirkpatrick (SK) model" \[1]. | +| [`Tsp`](/docs/api/qiskit-addon-opt-mapper/applications-tsp "qiskit_addon_opt_mapper.applications.Tsp") | Optimization application for the "traveling salesman problem" \[1] based on a NetworkX graph. | +| [`VehicleRouting`](/docs/api/qiskit-addon-opt-mapper/applications-vehicle-routing "qiskit_addon_opt_mapper.applications.VehicleRouting") | Optimization application for the "vehicle routing problem" \[1] based on a `NetworkX` graph. | +| [`VertexCover`](/docs/api/qiskit-addon-opt-mapper/applications-vertex-cover "qiskit_addon_opt_mapper.applications.VertexCover") | Optimization application for the "vertex cover" \[1] problem based on a NetworkX graph. | + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-binary-to-linear-binary.mdx b/docs/api/qiskit-addon-opt-mapper/converters-binary-to-linear-binary.mdx new file mode 100644 index 00000000000..190ce3d3f66 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-binary-to-linear-binary.mdx @@ -0,0 +1,114 @@ +--- +title: BinaryToLinearBinary (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.BinaryToLinearBinary in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.BinaryToLinearBinary +--- + + + +# qiskit\_addon\_opt\_mapper.converters.BinaryToLinearBinary + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert all high-degree terms to linear terms, both in objective and constraints. The converter assumes that the problem only contains binary variables. + + The conversion of a term `x1*x2` is done through the additional binary variable `x1ANDx2 = x1*x2` defined via: `` ` x1ANDx2 <= x1 x1ANDx2 <= x2 x1ANDx2 >= x1 + x2 - 1 ` `` + + Similarly, a term `x1*x2*x3` is converted via the additional binary variable `x1ANDx2ANDx3 = x1*x2*x3` defined via: `` ` x1ANDx2ANDx3 <= x1 x1ANDx2ANDx3 <= x2 x1ANDx2ANDx3 <= x3 x1ANDx2ANDx3 >= x1 + x2 + x3 - 2 ` `` + + The definition of the new variables always depends directly on the original variables. For instance, even if `x1ANDx2` is included in a problem, the definition of `x1ANDx2ANDx3` will be based on individual `x1`, `x2` and `x3`, and will not exploit `x1ANDx2`. Such an optimization may be handled by pre-solvers of commercial solvers, according to the specific needs. + + Note that during the conversion, powers of a single variable are removed: for instance, the expression `x0^2 + x1*x2^3` is treated as `x0 + x1*x2` + + Initialize converter. + + ### \_\_init\_\_ + + + Initialize converter. + + **Return type** + + None + + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.__init__ "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.__init__")() | Initialize converter. | + | [`convert`](#qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.convert "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.convert")(problem) | Convert all high-degree terms (namely, degree 2+) to linear terms. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.get_compatibility_msg "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.get_compatibility_msg")(problem) | Checks whether the given problem is compatible with the conversion. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.interpret "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.interpret")(x) | Convert a solution of the converted problem back to a solution of the original problem. | + | [`is_compatible`](#qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.is_compatible "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + + ### convert + + + Convert all high-degree terms (namely, degree 2+) to linear terms. + + **Parameters** + + **problem** (*OptimizationProblem*) + + **Return type** + + *OptimizationProblem* + + + ### get\_compatibility\_msg + + + Checks whether the given problem is compatible with the conversion. + + A problem is compatible if all variables are binary. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + A message describing the incompatibility, if any, or an empty string. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### interpret + + + Convert a solution of the converted problem back to a solution of the original problem. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-binary-to-spin.mdx b/docs/api/qiskit-addon-opt-mapper/converters-binary-to-spin.mdx new file mode 100644 index 00000000000..f372e666216 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-binary-to-spin.mdx @@ -0,0 +1,70 @@ +--- +title: BinaryToSpin (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.BinaryToSpin in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.BinaryToSpin +--- + + + +# qiskit\_addon\_opt\_mapper.converters.BinaryToSpin + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert all binary variables in the problem to spin variables. + + The conversion is done by the relation b\_i = (1 - s\_i)/2 where b\_i is a binary variable and s\_i is a spin variable. + + Initialize converter. + + ### \_\_init\_\_ + + + Initialize converter. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.BinaryToSpin.__init__ "qiskit_addon_opt_mapper.converters.BinaryToSpin.__init__")() | Initialize converter. | + | [`convert`](#qiskit_addon_opt_mapper.converters.BinaryToSpin.convert "qiskit_addon_opt_mapper.converters.BinaryToSpin.convert")(problem) | Convert all binary variables in the problem to spin variables. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.BinaryToSpin.interpret "qiskit_addon_opt_mapper.converters.BinaryToSpin.interpret")(x) | Convert a solution of the converted problem back to a solution of the original problem. | + + ### convert + + + Convert all binary variables in the problem to spin variables. + + **Parameters** + + **problem** (*OptimizationProblem*) + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert a solution of the converted problem back to a solution of the original problem. + + For binaries that became spins, we use b = (1 - s)/2. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-inequality-to-equality.mdx b/docs/api/qiskit-addon-opt-mapper/converters-inequality-to-equality.mdx new file mode 100644 index 00000000000..66e5fc0a4d6 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-inequality-to-equality.mdx @@ -0,0 +1,127 @@ +--- +title: InequalityToEquality (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.InequalityToEquality in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.InequalityToEquality +--- + + + +# qiskit\_addon\_opt\_mapper.converters.InequalityToEquality + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert inequality constraints into equality constraints by introducing slack variables. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.converters import InequalityToEquality + >>> problem = OptimizationProblem() + >>> # define a problem + >>> conv = InequalityToEquality() + >>> problem2 = conv.convert(problem) + ``` + + Init method. + + **Parameters** + + **mode** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – + + To choose the type of slack variables. There are 3 options for mode. + + * ’integer’: All slack variables will be integer variables. + * ’continuous’: All slack variables will be continuous variables. + * ’auto’: Use integer variables if possible, otherwise use continuous variables. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **mode** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – + + To choose the type of slack variables. There are 3 options for mode. + + * ’integer’: All slack variables will be integer variables. + * ’continuous’: All slack variables will be continuous variables. + * ’auto’: Use integer variables if possible, otherwise use continuous variables. + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.InequalityToEquality.__init__ "qiskit_addon_opt_mapper.converters.InequalityToEquality.__init__")(\[mode]) | Init method. | + | [`convert`](#qiskit_addon_opt_mapper.converters.InequalityToEquality.convert "qiskit_addon_opt_mapper.converters.InequalityToEquality.convert")(problem) | Convert a problem with inequality constraints into one with only equality constraints. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.InequalityToEquality.interpret "qiskit_addon_opt_mapper.converters.InequalityToEquality.interpret")(x) | Convert a result of a converted problem into that of the original problem. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | + | [`mode`](#qiskit_addon_opt_mapper.converters.InequalityToEquality.mode "qiskit_addon_opt_mapper.converters.InequalityToEquality.mode") | Returns the mode of the converter. | + + ### convert + + + Convert a problem with inequality constraints into one with only equality constraints. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved, that may contain inequality constraints. + + **Returns** + + The converted problem, that contain only equality constraints. + + **Raises** + + * **OptimizationError** – If a variable type is not supported. + * **OptimizationError** – If an unsupported mode is selected. + * **OptimizationError** – If an unsupported sense is specified. + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert a result of a converted problem into that of the original problem. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem or the given result in case of FAILURE. + + **Returns** + + The result of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### mode + + + Returns the mode of the converter. + + **Returns** + + The mode of the converter used for additional slack variables + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-integer-to-binary.mdx b/docs/api/qiskit-addon-opt-mapper/converters-integer-to-binary.mdx new file mode 100644 index 00000000000..ac1da6bd0cb --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-integer-to-binary.mdx @@ -0,0 +1,99 @@ +--- +title: IntegerToBinary (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.IntegerToBinary in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.IntegerToBinary +--- + + + +# qiskit\_addon\_opt\_mapper.converters.IntegerToBinary + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Integer to binary converter. + + Convert a `OptimizationProblem` into new one by encoding integers with binary variables. + + This bounded-coefficient encoding used in this converted is proposed in \[1], Eq. (5). + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.converters import IntegerToBinary + >>> problem = OptimizationProblem() + >>> var = problem.integer_var(name='x', lowerbound=0, upperbound=10) + >>> conv = IntegerToBinary() + >>> problem2 = conv.convert(problem) + ``` + + **References** + + **\[1]: Sahar Karimi, Pooya Ronagh (2017), Practical Integer-to-Binary Mapping for Quantum** + + Annealers. arxiv.org:1706.01945. + + Class initializer. + + ### \_\_init\_\_ + + + Class initializer. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | + | [`__init__`](#qiskit_addon_opt_mapper.converters.IntegerToBinary.__init__ "qiskit_addon_opt_mapper.converters.IntegerToBinary.__init__")() | Class initializer. | + | [`convert`](#qiskit_addon_opt_mapper.converters.IntegerToBinary.convert "qiskit_addon_opt_mapper.converters.IntegerToBinary.convert")(problem) | Convert an integer problem into a new problem with binary variables. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.IntegerToBinary.interpret "qiskit_addon_opt_mapper.converters.IntegerToBinary.interpret")(x) | Convert back the converted problem (binary variables) to the original (integer variables). | + + ### convert + + + Convert an integer problem into a new problem with binary variables. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved, that may contain integer variables. + + **Returns** + + The converted problem, that contains no integer variables. + + **Raises** + + **OptimizationError** – if variable or constraint type is not supported. + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert back the converted problem (binary variables) to the original (integer variables). + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem or the given result in case of FAILURE. + + **Returns** + + The result of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-linear-inequality-to-penalty.mdx b/docs/api/qiskit-addon-opt-mapper/converters-linear-inequality-to-penalty.mdx new file mode 100644 index 00000000000..3ac7f70b351 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-linear-inequality-to-penalty.mdx @@ -0,0 +1,143 @@ +--- +title: LinearInequalityToPenalty (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty +--- + + + +# qiskit\_addon\_opt\_mapper.converters.LinearInequalityToPenalty + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert linear inequality constraints to penalty terms of the objective function. + + There are some linear constraints which do not require slack variables to construct penalty terms \[1]. This class supports the following inequality constraints. + +$$ +\begin{split}\begin{array}{} +\text { Inequality constraint } & & \text { Penalty term } \\ +x \leq y & \rightarrow & P(x-x y) \\ +x \geq y & \rightarrow & P(y-x y) \\ +\sum_{i=1}^n x_i \leq 1, n \geq 2 & \rightarrow & +P \sum_{i, j : i < j} x_i x_j\\ +\sum_{i=1}^n x_i \geq n-1, n \geq 2 & \rightarrow & +P \sum_{i, j : i < j} (1 - x_i) (1 - x_j) +\end{array}\end{split} +$$ + + Note that x, y, z and $x_i$ are binary variables, and P is a penalty factor, where the value of P is automatically determined or supplied by users. + + If constraints match with any of the patterns, they are converted into penalty terms and added to the objective function. Otherwise, constraints are kept as is. + + **References** + + **\[1]: Fred Glover, et al. (2019),** + + A Tutorial on Formulating and Using QUBO Models, [arXiv:1811.11538](https://arxiv.org/abs/1811.11538). + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.__init__ "qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.__init__")(\[penalty]) | Init method. | + | [`convert`](#qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.convert "qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.convert")(problem) | Convert inequality constraints into penalty terms of the objective function. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.interpret "qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.interpret")(x) | Convert the result of the converted problem back to that of the original problem. | + + ## Attributes + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | + | [`penalty`](#qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.penalty "qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty.penalty") | Returns the penalty factor used in conversion. | + + ### convert + + + Convert inequality constraints into penalty terms of the objective function. + + This methods converts the following patterns where x, y, and $x_i$ are binary variables and P is a penalty factor. + +$$ +\begin{split}\begin{array}{} +\text { Inequality constraint } & & \text { Penalty term } \\ +x \leq y & \rightarrow & P(x-x y) \\ +x \geq y & \rightarrow & P(y-x y) \\ +\sum_{i=1}^n x_i \leq 1, n \geq 2 & \rightarrow & P \sum_{i, j : i < j} x_i x_j\\ +\sum_{i=1}^n x_i \geq n-1, n \geq 2 & \rightarrow & +P \sum_{i, j : i < j} (1 - x_i) (1 - x_j) +\end{array}\end{split} +$$ + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved. + + **Returns** + + The converted problem + + **Raises** + + **OptimizationError** – If an unsupported-type variable exists. + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert the result of the converted problem back to that of the original problem. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem or the given result in case of FAILURE. + + **Returns** + + The result of the original problem. + + **Raises** + + **OptimizationError** – if the number of variables in the result differs from that of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### penalty + + + Returns the penalty factor used in conversion. + + **Returns** + + The penalty factor used in conversion. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-maximize-to-minimize.mdx b/docs/api/qiskit-addon-opt-mapper/converters-maximize-to-minimize.mdx new file mode 100644 index 00000000000..868e41eec3c --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-maximize-to-minimize.mdx @@ -0,0 +1,78 @@ +--- +title: MaximizeToMinimize (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.MaximizeToMinimize in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.MaximizeToMinimize +--- + + + +# qiskit\_addon\_opt\_mapper.converters.MaximizeToMinimize + + + Bases: `_FlipProblemSense` + + Convert a maximization problem to a minimization problem only if it is a maximization problem. + + Otherwise problem’s sense is unchanged. + + ### \_\_init\_\_ + + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.MaximizeToMinimize.__init__ "qiskit_addon_opt_mapper.converters.MaximizeToMinimize.__init__")() | | + | [`convert`](#qiskit_addon_opt_mapper.converters.MaximizeToMinimize.convert "qiskit_addon_opt_mapper.converters.MaximizeToMinimize.convert")(problem) | Flip the sense of a problem. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.MaximizeToMinimize.interpret "qiskit_addon_opt_mapper.converters.MaximizeToMinimize.interpret")(x) | Convert the result of the converted problem back to that of the original problem. | + + ### convert + + + Flip the sense of a problem. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be flipped. + + **Returns** + + A converted problem, that has the flipped sense. + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert the result of the converted problem back to that of the original problem. + + Note: This implementation does not modify the result, but the method is required because the base class defines interpret as an abstract method. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem or the given result in case of FAILURE. + + **Returns** + + The result of the original problem. + + **Raises** + + **OptimizationError** – if the number of variables in the result differs from that of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-minimize-to-maximize.mdx b/docs/api/qiskit-addon-opt-mapper/converters-minimize-to-maximize.mdx new file mode 100644 index 00000000000..ed2bd24be94 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-minimize-to-maximize.mdx @@ -0,0 +1,78 @@ +--- +title: MinimizeToMaximize (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.MinimizeToMaximize in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.MinimizeToMaximize +--- + + + +# qiskit\_addon\_opt\_mapper.converters.MinimizeToMaximize + + + Bases: `_FlipProblemSense` + + Convert a minimization problem to a maximization problem only if it is a minimization problem. + + Otherwise problem’s sense is unchanged. + + ### \_\_init\_\_ + + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.MinimizeToMaximize.__init__ "qiskit_addon_opt_mapper.converters.MinimizeToMaximize.__init__")() | | + | [`convert`](#qiskit_addon_opt_mapper.converters.MinimizeToMaximize.convert "qiskit_addon_opt_mapper.converters.MinimizeToMaximize.convert")(problem) | Flip the sense of a problem. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.MinimizeToMaximize.interpret "qiskit_addon_opt_mapper.converters.MinimizeToMaximize.interpret")(x) | Convert the result of the converted problem back to that of the original problem. | + + ### convert + + + Flip the sense of a problem. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be flipped. + + **Returns** + + A converted problem, that has the flipped sense. + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert the result of the converted problem back to that of the original problem. + + Note: This implementation does not modify the result, but the method is required because the base class defines interpret as an abstract method. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem or the given result in case of FAILURE. + + **Returns** + + The result of the original problem. + + **Raises** + + **OptimizationError** – if the number of variables in the result differs from that of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-converter.mdx b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-converter.mdx new file mode 100644 index 00000000000..0d6268d1e28 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-converter.mdx @@ -0,0 +1,60 @@ +--- +title: OptimizationProblemConverter (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.OptimizationProblemConverter in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.OptimizationProblemConverter +--- + + + +# qiskit\_addon\_opt\_mapper.converters.OptimizationProblemConverter + + + Bases: [`ABC`](https://docs.python.org/3/library/abc.html#abc.ABC) + + An abstract class for converters of optimization problem in Qiskit optimization module. + + ### \_\_init\_\_ + + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.__init__ "qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.__init__")() | | + | [`convert`](#qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.convert "qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.convert")(problem) | Convert method. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.interpret "qiskit_addon_opt_mapper.converters.OptimizationProblemConverter.interpret")(x) | Interpret a result into another form using the information of conversion. | + + ### convert + + + Convert method. + + Convert a OptimizationProblem into another form and keep the information required to interpret the result. + + **Parameters** + + **problem** (*OptimizationProblem*) + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Interpret a result into another form using the information of conversion. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-hubo.mdx b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-hubo.mdx new file mode 100644 index 00000000000..32946a02895 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-hubo.mdx @@ -0,0 +1,161 @@ +--- +title: OptimizationProblemToHubo (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo +--- + + + +# qiskit\_addon\_opt\_mapper.converters.OptimizationProblemToHubo + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert an optimization problem into a HUBO form. + + HUBO stands for “higher-order unconstrained binary optimization”. The conversion is achieved by converting variables to binary and eliminating constraints. The resulting problem has no constraints and a higher-order polynomial objective function. This combines several converters: IntegerToBinary, InequalityToPenalty, EqualityToPenalty, and MaximizeToMinimize, while preserving higher-order terms in the objective function. The resulting HUBO problem can be directly mapped to an Ising Hamiltonian using the to\_ising() function. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.converters import OptimizationProblemToHubo + >>> problem = OptimizationProblem() + >>> # define a problem + >>> conv = OptimizationProblemToHubo() + >>> problem2 = conv.convert(problem) + ``` + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.__init__ "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.__init__")(\[penalty]) | Init method. | + | [`convert`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.convert "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.convert")(problem) | Convert an optimization problem into a HUBO form. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.get_compatibility_msg "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.get_compatibility_msg")(problem) | Checks whether the given problem is compatible with HUBO conversion. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.interpret "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.interpret")(x) | Convert the result of the converted problem back to that of the original problem. | + | [`is_compatible`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.is_compatible "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + + ## Attributes + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | + | [`penalty`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.penalty "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo.penalty") | Returns the penalty factor used in conversion. | + + ### convert + + + Convert an optimization problem into a HUBO form. + + The new problem has no constraints and the objective function is higher order polynomial. + + **Parameters** + + **problem** (*OptimizationProblem*) – An optimization problem to be converted. + + **Returns** + + A new optimization problem in the HUBO form. + + **Raises** + + **OptimizationError** – If the input problem is invalid. + + **Return type** + + *OptimizationProblem* + + + ### get\_compatibility\_msg + + + Checks whether the given problem is compatible with HUBO conversion. + + A problem is compatible if it can be converted to a HUBO (Higher-order Unconstrained Binary Optimization). If not, this function returns a message explaining the incompatibility. + + The following problems are not compatible: - Continuous variables are not supported. - Constraints with float coefficients are not supported, because inequality constraints cannot be converted to equality constraints using integer slack variables. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + A message describing the incompatibility. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### interpret + + + Convert the result of the converted problem back to that of the original problem. + + Done by applying the interpret method of each converter in reverse order. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – A solution vector of the converted problem. + + **Returns** + + A solution vector of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### penalty + + + Returns the penalty factor used in conversion. + + **Returns** + + The penalty factor used in conversion. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-qubo.mdx b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-qubo.mdx new file mode 100644 index 00000000000..a6e6adc4752 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-qubo.mdx @@ -0,0 +1,161 @@ +--- +title: OptimizationProblemToQubo (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo +--- + + + +# qiskit\_addon\_opt\_mapper.converters.OptimizationProblemToQubo + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert a given optimization problem in quadratic form into a QUBO problem. + + Done by converting variables to binary and eliminating constraints. An optimization problem in quadratic form is a problem with quadratic objective function and linear constraints. A QUBO is a problem with quadratic objective function and no constraints. This combines several converters: IntegerToBinary, InequalityToPenalty, InequalityToEquality, EqualityToPenalty, and MaximizeToMinimize. The resulting HUBO problem can be directly mapped to an Ising Hamiltonian using the to\_ising() function. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo + >>> problem = OptimizationProblem() + >>> # define a problem + >>> conv = OptimizationProblemToQubo() + >>> problem2 = conv.convert(problem) + ``` + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + **penalty** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – Penalty factor to scale equality constraints that are added to objective. If None is passed, a penalty factor will be automatically calculated on every conversion. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.__init__ "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.__init__")(\[penalty]) | Init method. | + | [`convert`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.convert "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.convert")(problem) | Convert a optimization problem into a QUBO form. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.get_compatibility_msg "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.get_compatibility_msg")(problem) | Checks whether a given problem can be converted to a QUBO form. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.interpret "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.interpret")(x) | Convert the result of the converted problem back to that of the original problem. | + | [`is_compatible`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.is_compatible "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + + ## Attributes + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | + | [`penalty`](#qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.penalty "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo.penalty") | Returns the penalty factor used in conversion. | + + ### convert + + + Convert a optimization problem into a QUBO form. + + The new problem has no constraints and the objective function is quadratic. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem with linear constraints to be solved. + + **Returns** + + The problem converted in QUBO format as minimization problem. + + **Raises** + + **OptimizationError** – In case of an incompatible problem. + + **Return type** + + *OptimizationProblem* + + + ### get\_compatibility\_msg + + + Checks whether a given problem can be converted to a QUBO form. + + If the problem is not compatible, this function returns a message explaining the incompatibility. + + The following problems are not compatible: - Continuous variables are not supported. - Higher-order objective functions are not supported. - Quadratic constraints are not supported. - Higher-order constraints are not supported. - Constraints with float coefficients are not supported, because inequality constraints cannot be converted to equality constraints using integer slack variables. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + A message describing the incompatibility. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### interpret + + + Convert the result of the converted problem back to that of the original problem. + + Done by applying the interpret method of each converter in reverse order. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The result of the converted problem. + + **Returns** + + The result of the original problem. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### penalty + + + Returns the penalty factor used in conversion. + + **Returns** + + The penalty factor used in conversion. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters-spin-to-binary.mdx b/docs/api/qiskit-addon-opt-mapper/converters-spin-to-binary.mdx new file mode 100644 index 00000000000..5ef111ba4d4 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters-spin-to-binary.mdx @@ -0,0 +1,76 @@ +--- +title: SpinToBinary (latest version) +description: API reference for qiskit_addon_opt_mapper.converters.SpinToBinary in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.converters.SpinToBinary +--- + + + +# qiskit\_addon\_opt\_mapper.converters.SpinToBinary + + + Bases: [`OptimizationProblemConverter`](converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.optimization_problem_converter.OptimizationProblemConverter") + + Convert all spin variables in the problem to binary variables. + + The conversion is done by the relation: + + ```python + s_i = 1 - 2 b_i + ``` + + where s\_i is a spin variable (in \{-1, +1}) and b\_i is a binary variable (in \{0, 1}). + + Class initializer. + + ### \_\_init\_\_ + + + Class initializer. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.converters.SpinToBinary.__init__ "qiskit_addon_opt_mapper.converters.SpinToBinary.__init__")() | Class initializer. | + | [`convert`](#qiskit_addon_opt_mapper.converters.SpinToBinary.convert "qiskit_addon_opt_mapper.converters.SpinToBinary.convert")(problem) | Convert all spin variables in the problem to binary variables. | + | [`interpret`](#qiskit_addon_opt_mapper.converters.SpinToBinary.interpret "qiskit_addon_opt_mapper.converters.SpinToBinary.interpret")(x) | Convert a solution of the converted (binary) problem back to the original (spin) space. | + + ### convert + + + Convert all spin variables in the problem to binary variables. + + **Parameters** + + **problem** (*OptimizationProblem*) + + **Return type** + + *OptimizationProblem* + + + ### interpret + + + Convert a solution of the converted (binary) problem back to the original (spin) space. + + For spins we use s = 1 - 2 b. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]*) + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + diff --git a/docs/api/qiskit-addon-opt-mapper/converters.mdx b/docs/api/qiskit-addon-opt-mapper/converters.mdx new file mode 100644 index 00000000000..f7cf580a68d --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/converters.mdx @@ -0,0 +1,47 @@ +--- +title: converters (latest version) +description: API reference for qiskit_addon_opt_mapper.converters in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_opt_mapper.converters +--- + + + + + +# Converters + +`qiskit_addon_opt_mapper.converters` + +Converters module for Optimization add-on. + + + +## Optimization converters . + +`qiskit_addon_opt_mapper.converters` + +This is a set of converters having convert functionality to go between different representations of a given `QuadraticProgram` and to interpret a given result for the problem, based on the original problem before conversion, to return an appropriate [`SolverResult`](/docs/api/qiskit-addon-opt-mapper/solvers-solver-result "qiskit_addon_opt_mapper.solvers.SolverResult"). + +### Base class for converters + +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| [`OptimizationProblemConverter`](/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-converter "qiskit_addon_opt_mapper.converters.OptimizationProblemConverter") | An abstract class for converters of optimization problem in Qiskit optimization module. | + +### Converters + +| | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`BinaryToLinearBinary`](/docs/api/qiskit-addon-opt-mapper/converters-binary-to-linear-binary "qiskit_addon_opt_mapper.converters.BinaryToLinearBinary") | Convert all high-degree terms to linear terms, both in objective and constraints. | +| [`BinaryToSpin`](/docs/api/qiskit-addon-opt-mapper/converters-binary-to-spin "qiskit_addon_opt_mapper.converters.BinaryToSpin") | Convert all binary variables in the problem to spin variables. | +| [`InequalityToEquality`](/docs/api/qiskit-addon-opt-mapper/converters-inequality-to-equality "qiskit_addon_opt_mapper.converters.InequalityToEquality") | Convert inequality constraints into equality constraints by introducing slack variables. | +| [`IntegerToBinary`](/docs/api/qiskit-addon-opt-mapper/converters-integer-to-binary "qiskit_addon_opt_mapper.converters.IntegerToBinary") | Integer to binary converter. | +| [`LinearInequalityToPenalty`](/docs/api/qiskit-addon-opt-mapper/converters-linear-inequality-to-penalty "qiskit_addon_opt_mapper.converters.LinearInequalityToPenalty") | Convert linear inequality constraints to penalty terms of the objective function. | +| [`MaximizeToMinimize`](/docs/api/qiskit-addon-opt-mapper/converters-maximize-to-minimize "qiskit_addon_opt_mapper.converters.MaximizeToMinimize") | Convert a maximization problem to a minimization problem only if it is a maximization problem. | +| [`MinimizeToMaximize`](/docs/api/qiskit-addon-opt-mapper/converters-minimize-to-maximize "qiskit_addon_opt_mapper.converters.MinimizeToMaximize") | Convert a minimization problem to a maximization problem only if it is a minimization problem. | +| [`OptimizationProblemToHubo`](/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-hubo "qiskit_addon_opt_mapper.converters.OptimizationProblemToHubo") | Convert an optimization problem into a HUBO form. | +| [`OptimizationProblemToQubo`](/docs/api/qiskit-addon-opt-mapper/converters-optimization-problem-to-qubo "qiskit_addon_opt_mapper.converters.OptimizationProblemToQubo") | Convert a given optimization problem in quadratic form into a QUBO problem. | +| [`SpinToBinary`](/docs/api/qiskit-addon-opt-mapper/converters-spin-to-binary "qiskit_addon_opt_mapper.converters.SpinToBinary") | Convert all spin variables in the problem to binary variables. | + diff --git a/docs/api/qiskit-addon-opt-mapper/index.mdx b/docs/api/qiskit-addon-opt-mapper/index.mdx new file mode 100644 index 00000000000..17f499260dc --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/index.mdx @@ -0,0 +1,15 @@ +--- +title: Optimization mapper API documentation (latest version) +description: Index of all the modules in the latest version of qiskit-addon-opt-mapper. +--- + + + +# `qiskit-addon-opt-mapper` API reference + +* [Optimization Application Modeling (`qiskit_addon_opt_mapper.applications`)](applications) +* [Converters (`qiskit_addon_opt_mapper.converters`)](converters) +* [Problem Modeling Tools (`qiskit_addon_opt_mapper.problems`)](problems) +* [Reference Solvers (`qiskit_addon_opt_mapper.solvers`)](solvers) +* [Translators (`qiskit_addon_opt_mapper.translators`)](translators) + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-constraint.mdx b/docs/api/qiskit-addon-opt-mapper/problems-constraint.mdx new file mode 100644 index 00000000000..57509c8e3c9 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-constraint.mdx @@ -0,0 +1,124 @@ +--- +title: Constraint (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.Constraint in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.Constraint +--- + + + +# qiskit\_addon\_opt\_mapper.problems.Constraint + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Abstract Constraint Class. + + Initializes the constraint. + + **Parameters** + + * **optimization\_problem** (*Any*) – The parent OptimizationProblem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + ### \_\_init\_\_ + + + Initializes the constraint. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.Constraint.__init__ "qiskit_addon_opt_mapper.problems.Constraint.__init__")(optimization\_problem, name, sense, rhs) | Initializes the constraint. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.Constraint.evaluate "qiskit_addon_opt_mapper.problems.Constraint.evaluate")(x) | Evaluate left-hand-side of constraint for given values of variables. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | + | [`name`](#qiskit_addon_opt_mapper.problems.Constraint.name "qiskit_addon_opt_mapper.problems.Constraint.name") | Returns the name of the constraint. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.Constraint.optimization_problem "qiskit_addon_opt_mapper.problems.Constraint.optimization_problem") | Returns the parent OptimizationProblem. | + | [`rhs`](#qiskit_addon_opt_mapper.problems.Constraint.rhs "qiskit_addon_opt_mapper.problems.Constraint.rhs") | Returns the right-hand-side of the constraint. | + | [`sense`](#qiskit_addon_opt_mapper.problems.Constraint.sense "qiskit_addon_opt_mapper.problems.Constraint.sense") | Returns the sense of the constraint. | + + ### Sense + + + alias of `ConstraintSense` + + + ### evaluate + + + Evaluate left-hand-side of constraint for given values of variables. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values to be used for the variables. + + **Returns** + + The left-hand-side of the constraint. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### name + + + Returns the name of the constraint. + + **Returns** + + The name of the constraint. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### rhs + + + Returns the right-hand-side of the constraint. + + **Returns** + + The right-hand-side of the constraint. + + + ### sense + + + Returns the sense of the constraint. + + **Returns** + + The sense of the constraint. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-higher-order-constraint.mdx b/docs/api/qiskit-addon-opt-mapper/problems-higher-order-constraint.mdx new file mode 100644 index 00000000000..f02db593f0f --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-higher-order-constraint.mdx @@ -0,0 +1,171 @@ +--- +title: HigherOrderConstraint (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.HigherOrderConstraint in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.HigherOrderConstraint +--- + + + +# qiskit\_addon\_opt\_mapper.problems.HigherOrderConstraint + + + Bases: [`Constraint`](problems-constraint "qiskit_addon_opt_mapper.problems.constraint.Constraint") + + Constraint in higher order form. + + e.g. `linear(x) + x^T Q x + sum_{k>=3}  sum_{|t|=k} C_k[t] * prod_{i in t} x[i]` `sense` `rhs` where `sense` is one of the ConstraintSense values (e.g., LE, \<=) and `rhs` is a float. + + Supports both a single higher-order term (order+coeffs) and multiple via higher\_orders=\{k: coeffs}. + + Construct a higher-order constraint with linear, quadratic, and optional higher-order parts. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The optimization problem this constraint belongs to. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – Coefficients for the linear part. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – Coefficients for the quadratic part. + * **higher\_order** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...],* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)*] | None*) – A single higher-order expression or a dictionary of \{order: coeffs} for multiple orders (k≥3). + * **sense** (*ConstraintSense*) – The sense of the constraint (e.g., LE, \<=). + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side value of the constraint. + + ### \_\_init\_\_ + + + Construct a higher-order constraint with linear, quadratic, and optional higher-order parts. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The optimization problem this constraint belongs to. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – Coefficients for the linear part. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – Coefficients for the quadratic part. + * **higher\_order** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...],* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)*] | None*) – A single higher-order expression or a dictionary of \{order: coeffs} for multiple orders (k≥3). + * **sense** (*ConstraintSense*) – The sense of the constraint (e.g., LE, \<=). + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side value of the constraint. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.__init__ "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.__init__")(optimization\_problem, name\[, ...]) | Construct a higher-order constraint with linear, quadratic, and optional higher-order parts. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.evaluate "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.evaluate")(x) | Evaluate the left-hand-side of the constraint. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | + | [`higher_order`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.higher_order "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.higher_order") | HigherOrderExpression}. | + | [`linear`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.linear "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.linear") | Returns the linear expression corresponding to the left-hand-side of the constraint. | + | [`name`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.name "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.name") | Returns the name of the constraint. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.optimization_problem "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.optimization_problem") | Returns the parent OptimizationProblem. | + | [`quadratic`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.quadratic "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.quadratic") | Returns the quadratic expression corresponding to the left-hand-side of the constraint. | + | [`rhs`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.rhs "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.rhs") | Returns the right-hand-side of the constraint. | + | [`sense`](#qiskit_addon_opt_mapper.problems.HigherOrderConstraint.sense "qiskit_addon_opt_mapper.problems.HigherOrderConstraint.sense") | Returns the sense of the constraint. | + + ### Sense + + + alias of `ConstraintSense` + + + ### evaluate + + + Evaluate the left-hand-side of the constraint. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The left-hand-side of the constraint given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### higher\_order + + + HigherOrderExpression}. + + **Returns** + + A dictionary mapping order (k>=3) to HigherOrderExpression. + + **Type** + + Return a shallow copy of \{order + + + ### linear + + + Returns the linear expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side linear expression. + + + ### name + + + Returns the name of the constraint. + + **Returns** + + The name of the constraint. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### quadratic + + + Returns the quadratic expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side quadratic expression. + + + ### rhs + + + Returns the right-hand-side of the constraint. + + **Returns** + + The right-hand-side of the constraint. + + + ### sense + + + Returns the sense of the constraint. + + **Returns** + + The sense of the constraint. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression.mdx b/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression.mdx new file mode 100644 index 00000000000..5ca140176ec --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression.mdx @@ -0,0 +1,170 @@ +--- +title: HigherOrderExpression (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.HigherOrderExpression in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.HigherOrderExpression +--- + + + +# qiskit\_addon\_opt\_mapper.problems.HigherOrderExpression + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Representation of a symmetric k-th order expression by its coefficients. + + We represent a symmetric polynomial term of order k as `f(x) = sum_{t} C[t] * prod_{i in t} x[i]`, where t is a multiset of variable indices of length k. + + When dealing with multidimensional array indices, the value is stored only at the lexicographically smallest permutation of the indices (obtained by sorting them in ascending order). + + For example, for a 4th-order term `2⋅x1⋅x2⋅x3⋅x4`, the coefficient “2” is stored at `dict((1, 2, 3, 4))`. Other permutations like `dict((2, 1, 4, 3))` or `dict((4, 3, 2, 1))` are left empty. + + Creates a new higher-order expression. + + **Parameters** + + * **optimization\_problem** (*Any*) – The parent OptimizationProblem. + * **coefficients** (*ndarray |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[Key,* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)) – Coefficients as either: - A dense ndarray or list with shape (n,)\*k, or - A dict mapping a tuple of variable indices/names (length k) to float. Keys are canonicalized to ascending order and summed. + + ### \_\_init\_\_ + + + Creates a new higher-order expression. + + **Parameters** + + * **optimization\_problem** (*Any*) – The parent OptimizationProblem. + * **coefficients** (*ndarray |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[Key,* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)) – Coefficients as either: - A dense ndarray or list with shape (n,)\*k, or - A dict mapping a tuple of variable indices/names (length k) to float. Keys are canonicalized to ascending order and summed. + + **Return type** + + None + + + ## Methods + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | + | [`__init__`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.__init__ "qiskit_addon_opt_mapper.problems.HigherOrderExpression.__init__")(optimization\_problem, coefficients) | Creates a new higher-order expression. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.evaluate "qiskit_addon_opt_mapper.problems.HigherOrderExpression.evaluate")(x) | Evaluate the expression: sum\_\{t} C\[t] \* prod\_\{i in t} x\[i]. | + | [`evaluate_gradient`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.evaluate_gradient "qiskit_addon_opt_mapper.problems.HigherOrderExpression.evaluate_gradient")(x) | Evaluate gradient wrt x. | + | [`to_array`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.to_array "qiskit_addon_opt_mapper.problems.HigherOrderExpression.to_array")(\[symmetric]) | Returns a dense tensor. | + | [`to_dict`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.to_dict "qiskit_addon_opt_mapper.problems.HigherOrderExpression.to_dict")(\[use\_name]) | Returns the internal coefficients as a dictionary. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | + | [`bounds`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.bounds "qiskit_addon_opt_mapper.problems.HigherOrderExpression.bounds") | Returns the lower bound and the upper bound of the linear expression. | + | [`coefficients`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.coefficients "qiskit_addon_opt_mapper.problems.HigherOrderExpression.coefficients") | Returns a copy of internal (canonical) coefficient dictionary. | + | [`num_variables`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.num_variables "qiskit_addon_opt_mapper.problems.HigherOrderExpression.num_variables") | Returns the number of variables in this expression. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.optimization_problem "qiskit_addon_opt_mapper.problems.HigherOrderExpression.optimization_problem") | Returns the parent OptimizationProblem. | + | [`order`](#qiskit_addon_opt_mapper.problems.HigherOrderExpression.order "qiskit_addon_opt_mapper.problems.HigherOrderExpression.order") | Returns the order of the polynomial (k >= 3). | + + ### bounds + + + Returns the lower bound and the upper bound of the linear expression. + + **Returns** + + The lower bound and the upper bound of the linear expression + + + ### coefficients + + + Returns a copy of internal (canonical) coefficient dictionary. + + + ### evaluate + + + Evaluate the expression: sum\_\{t} C\[t] \* prod\_\{i in t} x\[i]. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the higher order expression given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### evaluate\_gradient + + + Evaluate gradient wrt x. + + For each m, g\[m] = sum\_\{t} C\[t] \* (count\_m\_in\_t) \* prod\_\{i in t / \{one m}}. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the gradient of the higher order expression given the variable values. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### num\_variables + + + Returns the number of variables in this expression. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### order + + + Returns the order of the polynomial (k >= 3). + + + ### to\_array + + + Returns a dense tensor. + + **Parameters** + + **symmetric** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – If False, returns a tensor with coefficients at the lexicographically smallest index only (others zero). If True, distributes coefficients equally over all permutations of the multiset key. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_dict + + + Returns the internal coefficients as a dictionary. + + **Parameters** + + **use\_name** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), …] | [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[str](https://docs.python.org/3/library/stdtypes.html#str), …], [float](https://docs.python.org/3/library/functions.html#float)] + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-linear-constraint.mdx b/docs/api/qiskit-addon-opt-mapper/problems-linear-constraint.mdx new file mode 100644 index 00000000000..ee0d7e29236 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-linear-constraint.mdx @@ -0,0 +1,137 @@ +--- +title: LinearConstraint (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.LinearConstraint in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.LinearConstraint +--- + + + +# qiskit\_addon\_opt\_mapper.problems.LinearConstraint + + + Bases: [`Constraint`](problems-constraint "qiskit_addon_opt_mapper.problems.constraint.Constraint") + + Representation of a linear constraint. + + Init method. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent optimization problem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent optimization problem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + **Return type** + + None + + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.LinearConstraint.__init__ "qiskit_addon_opt_mapper.problems.LinearConstraint.__init__")(optimization\_problem, name, linear, ...) | Init method. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.LinearConstraint.evaluate "qiskit_addon_opt_mapper.problems.LinearConstraint.evaluate")(x) | Evaluate the left-hand-side of the constraint. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | + | [`linear`](#qiskit_addon_opt_mapper.problems.LinearConstraint.linear "qiskit_addon_opt_mapper.problems.LinearConstraint.linear") | Returns the linear expression corresponding to the left-hand-side of the constraint. | + | [`name`](#qiskit_addon_opt_mapper.problems.LinearConstraint.name "qiskit_addon_opt_mapper.problems.LinearConstraint.name") | Returns the name of the constraint. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.LinearConstraint.optimization_problem "qiskit_addon_opt_mapper.problems.LinearConstraint.optimization_problem") | Returns the parent OptimizationProblem. | + | [`rhs`](#qiskit_addon_opt_mapper.problems.LinearConstraint.rhs "qiskit_addon_opt_mapper.problems.LinearConstraint.rhs") | Returns the right-hand-side of the constraint. | + | [`sense`](#qiskit_addon_opt_mapper.problems.LinearConstraint.sense "qiskit_addon_opt_mapper.problems.LinearConstraint.sense") | Returns the sense of the constraint. | + + ### Sense + + + alias of `ConstraintSense` + + + ### evaluate + + + Evaluate the left-hand-side of the constraint. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The left-hand-side of the constraint given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### linear + + + Returns the linear expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side linear expression. + + + ### name + + + Returns the name of the constraint. + + **Returns** + + The name of the constraint. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### rhs + + + Returns the right-hand-side of the constraint. + + **Returns** + + The right-hand-side of the constraint. + + + ### sense + + + Returns the sense of the constraint. + + **Returns** + + The sense of the constraint. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-linear-expression.mdx b/docs/api/qiskit-addon-opt-mapper/problems-linear-expression.mdx new file mode 100644 index 00000000000..739e6c4208e --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-linear-expression.mdx @@ -0,0 +1,166 @@ +--- +title: LinearExpression (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.LinearExpression in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.LinearExpression +--- + + + +# qiskit\_addon\_opt\_mapper.problems.LinearExpression + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Representation of a linear expression by its coefficients. + + Creates a new linear expression. + + The linear expression can be defined via an array, a list, a sparse matrix, or a dictionary that uses variable names or indices as keys and stores the values internally as a dok\_matrix. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **coefficients** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The (sparse) representation of the coefficients. + + ### \_\_init\_\_ + + + Creates a new linear expression. + + The linear expression can be defined via an array, a list, a sparse matrix, or a dictionary that uses variable names or indices as keys and stores the values internally as a dok\_matrix. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **coefficients** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The (sparse) representation of the coefficients. + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.LinearExpression.__init__ "qiskit_addon_opt_mapper.problems.LinearExpression.__init__")(optimization\_problem, coefficients) | Creates a new linear expression. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.LinearExpression.evaluate "qiskit_addon_opt_mapper.problems.LinearExpression.evaluate")(x) | Evaluate the linear expression for given variables. | + | [`evaluate_gradient`](#qiskit_addon_opt_mapper.problems.LinearExpression.evaluate_gradient "qiskit_addon_opt_mapper.problems.LinearExpression.evaluate_gradient")(x) | Evaluate the gradient of the linear expression for given variables. | + | [`to_array`](#qiskit_addon_opt_mapper.problems.LinearExpression.to_array "qiskit_addon_opt_mapper.problems.LinearExpression.to_array")() | Returns the coefficients of the linear expression as array. | + | [`to_dict`](#qiskit_addon_opt_mapper.problems.LinearExpression.to_dict "qiskit_addon_opt_mapper.problems.LinearExpression.to_dict")(\[use\_name]) | Returns the coefficients of the linear expression as dictionary. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | + | [`bounds`](#qiskit_addon_opt_mapper.problems.LinearExpression.bounds "qiskit_addon_opt_mapper.problems.LinearExpression.bounds") | Returns the lower bound and the upper bound of the linear expression. | + | [`coefficients`](#qiskit_addon_opt_mapper.problems.LinearExpression.coefficients "qiskit_addon_opt_mapper.problems.LinearExpression.coefficients") | Returns the coefficients of the linear expression. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.LinearExpression.optimization_problem "qiskit_addon_opt_mapper.problems.LinearExpression.optimization_problem") | Returns the parent OptimizationProblem. | + + ### bounds + + + Returns the lower bound and the upper bound of the linear expression. + + **Returns** + + The lower bound and the upper bound of the linear expression + + **Raises** + + **OptimizationError** – if the linear expression contains any unbounded variable + + + ### coefficients + + + Returns the coefficients of the linear expression. + + **Returns** + + The coefficients of the linear expression. + + + ### evaluate + + + Evaluate the linear expression for given variables. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the linear expression given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### evaluate\_gradient + + + Evaluate the gradient of the linear expression for given variables. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the gradient of the linear expression given the variable values. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### to\_array + + + Returns the coefficients of the linear expression as array. + + **Returns** + + An array with the coefficients corresponding to the linear expression. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_dict + + + Returns the coefficients of the linear expression as dictionary. + + Either using variable names or indices as keys. + + **Parameters** + + **use\_name** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Determines whether to use index or names to refer to variables. + + **Returns** + + An dictionary with the coefficients corresponding to the linear expression. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[int](https://docs.python.org/3/library/functions.html#int) | [str](https://docs.python.org/3/library/stdtypes.html#str), [float](https://docs.python.org/3/library/functions.html#float)] + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-optimization-objective.mdx b/docs/api/qiskit-addon-opt-mapper/problems-optimization-objective.mdx new file mode 100644 index 00000000000..a9aad06c8ce --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-optimization-objective.mdx @@ -0,0 +1,177 @@ +--- +title: OptimizationObjective (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.OptimizationObjective in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.OptimizationObjective +--- + + + +# qiskit\_addon\_opt\_mapper.problems.OptimizationObjective + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Optimization objective element. + + **Follows:** + + constant + linear(x) + x^T Q x + sum\_\{k>=3} H\_k(x). + + Construct an objective with linear, quadratic, and optional higher-order parts. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The optimization problem this objective belongs to. + * **constant** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The constant part of the objective function. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – The coefficients for the linear part of the objective function. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – The coefficients for the quadratic part of the objective function. + * **higher\_order** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*, ...],* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)*] | None*) – A single higher-order expression or a dictionary of \{order: coeffs} for multiple orders (k>=3). + * **sense** (*ObjSense*) – The sense of the objective function (e.g., MINIMIZE, MAXIMIZE). + + ### \_\_init\_\_ + + + Construct an objective with linear, quadratic, and optional higher-order parts. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The optimization problem this objective belongs to. + * **constant** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The constant part of the objective function. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – The coefficients for the linear part of the objective function. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*] | None*) – The coefficients for the quadratic part of the objective function. + * **higher\_order** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...],* [*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*list*](https://docs.python.org/3/library/stdtypes.html#list)*] | None*) – A single higher-order expression or a dictionary of \{order: coeffs} for multiple orders (k>=3). + * **sense** (*ObjSense*) – The sense of the objective function (e.g., MINIMIZE, MAXIMIZE). + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.__init__ "qiskit_addon_opt_mapper.problems.OptimizationObjective.__init__")(optimization\_problem\[, constant, ...]) | Construct an objective with linear, quadratic, and optional higher-order parts. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.evaluate "qiskit_addon_opt_mapper.problems.OptimizationObjective.evaluate")(x) | Evaluate objective value at x. | + | [`evaluate_gradient`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.evaluate_gradient "qiskit_addon_opt_mapper.problems.OptimizationObjective.evaluate_gradient")(x) | Evaluate gradient of the objective at x. | + + ## Attributes + + | | | + | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | + | [`constant`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.constant "qiskit_addon_opt_mapper.problems.OptimizationObjective.constant") | Returns the constant part of the objective function. | + | [`higher_order`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.higher_order "qiskit_addon_opt_mapper.problems.OptimizationObjective.higher_order") | HigherOrderExpression}. | + | [`linear`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.linear "qiskit_addon_opt_mapper.problems.OptimizationObjective.linear") | Returns the linear expression corresponding to the left-hand-side of the constraint. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.optimization_problem "qiskit_addon_opt_mapper.problems.OptimizationObjective.optimization_problem") | Returns the parent OptimizationProblem. | + | [`quadratic`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.quadratic "qiskit_addon_opt_mapper.problems.OptimizationObjective.quadratic") | Returns the quadratic expression corresponding to the left-hand-side of the constraint. | + | [`sense`](#qiskit_addon_opt_mapper.problems.OptimizationObjective.sense "qiskit_addon_opt_mapper.problems.OptimizationObjective.sense") | Returns the sense of the objective function. | + + ### Sense + + + alias of `ObjSense` + + + ### constant + + + Returns the constant part of the objective function. + + **Returns** + + The constant part of the objective function. + + + ### evaluate + + + Evaluate objective value at x. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The objective value given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### evaluate\_gradient + + + Evaluate gradient of the objective at x. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*int*](https://docs.python.org/3/library/functions.html#int) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The gradient of the objective function given the variable values. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### higher\_order + + + HigherOrderExpression}. + + **Returns** + + A dictionary mapping order (k>=3) to HigherOrderExpression. + + **Type** + + Return a shallow copy of \{order + + + ### linear + + + Returns the linear expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side linear expression. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### quadratic + + + Returns the quadratic expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side quadratic expression. + + + ### sense + + + Returns the sense of the objective function. + + **Returns** + + The sense of the objective function (e.g., MINIMIZE, MAXIMIZE). + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-optimization-problem-element.mdx b/docs/api/qiskit-addon-opt-mapper/problems-optimization-problem-element.mdx new file mode 100644 index 00000000000..51b14874b46 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-optimization-problem-element.mdx @@ -0,0 +1,68 @@ +--- +title: OptimizationProblemElement (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.OptimizationProblemElement in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.OptimizationProblemElement +--- + + + +# qiskit\_addon\_opt\_mapper.problems.OptimizationProblemElement + + + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) + + Interface class for all objects that have a parent OptimizationProblem. + + Initialize object with parent OptimizationProblem. + + **Parameters** + + **optimization\_problem** (*problems.OptimizationProblem*) – The parent OptimizationProblem. + + **Raises** + + [**TypeError**](https://docs.python.org/3/library/exceptions.html#TypeError) – OptimizationProblem instance expected. + + ### \_\_init\_\_ + + + Initialize object with parent OptimizationProblem. + + **Parameters** + + **optimization\_problem** (*OptimizationProblem*) – The parent OptimizationProblem. + + **Raises** + + [**TypeError**](https://docs.python.org/3/library/exceptions.html#TypeError) – OptimizationProblem instance expected. + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.OptimizationProblemElement.__init__ "qiskit_addon_opt_mapper.problems.OptimizationProblemElement.__init__")(optimization\_problem) | Initialize object with parent OptimizationProblem. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.OptimizationProblemElement.optimization_problem "qiskit_addon_opt_mapper.problems.OptimizationProblemElement.optimization_problem") | Returns the parent OptimizationProblem. | + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-quadratic-constraint.mdx b/docs/api/qiskit-addon-opt-mapper/problems-quadratic-constraint.mdx new file mode 100644 index 00000000000..899a9a15b42 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-quadratic-constraint.mdx @@ -0,0 +1,150 @@ +--- +title: QuadraticConstraint (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.QuadraticConstraint in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.QuadraticConstraint +--- + + + +# qiskit\_addon\_opt\_mapper.problems.QuadraticConstraint + + + Bases: [`Constraint`](problems-constraint "qiskit_addon_opt_mapper.problems.constraint.Constraint") + + Representation of a quadratic constraint. + + Constructs a quadratic constraint, consisting of a linear and a quadratic term. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent optimization problem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear part of the constraint. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear part of the constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + ### \_\_init\_\_ + + + Constructs a quadratic constraint, consisting of a linear and a quadratic term. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent optimization problem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The name of the constraint. + * **linear** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear part of the constraint. + * **quadratic** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The coefficients specifying the linear part of the constraint. + * **sense** (*ConstraintSense*) – The sense of the constraint. + * **rhs** ([*float*](https://docs.python.org/3/library/functions.html#float)) – The right-hand-side of the constraint. + + **Return type** + + None + + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.__init__ "qiskit_addon_opt_mapper.problems.QuadraticConstraint.__init__")(optimization\_problem, name, linear, ...) | Constructs a quadratic constraint, consisting of a linear and a quadratic term. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.evaluate "qiskit_addon_opt_mapper.problems.QuadraticConstraint.evaluate")(x) | Evaluate the left-hand-side of the constraint. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | + | [`linear`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.linear "qiskit_addon_opt_mapper.problems.QuadraticConstraint.linear") | Returns the linear expression corresponding to the left-hand-side of the constraint. | + | [`name`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.name "qiskit_addon_opt_mapper.problems.QuadraticConstraint.name") | Returns the name of the constraint. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.optimization_problem "qiskit_addon_opt_mapper.problems.QuadraticConstraint.optimization_problem") | Returns the parent OptimizationProblem. | + | [`quadratic`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.quadratic "qiskit_addon_opt_mapper.problems.QuadraticConstraint.quadratic") | Returns the quadratic expression corresponding to the left-hand-side of the constraint. | + | [`rhs`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.rhs "qiskit_addon_opt_mapper.problems.QuadraticConstraint.rhs") | Returns the right-hand-side of the constraint. | + | [`sense`](#qiskit_addon_opt_mapper.problems.QuadraticConstraint.sense "qiskit_addon_opt_mapper.problems.QuadraticConstraint.sense") | Returns the sense of the constraint. | + + ### Sense + + + alias of `ConstraintSense` + + + ### evaluate + + + Evaluate the left-hand-side of the constraint. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The left-hand-side of the constraint given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### linear + + + Returns the linear expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side linear expression. + + + ### name + + + Returns the name of the constraint. + + **Returns** + + The name of the constraint. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### quadratic + + + Returns the quadratic expression corresponding to the left-hand-side of the constraint. + + **Returns** + + The left-hand-side quadratic expression. + + + ### rhs + + + Returns the right-hand-side of the constraint. + + **Returns** + + The right-hand-side of the constraint. + + + ### sense + + + Returns the sense of the constraint. + + **Returns** + + The sense of the constraint. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-quadratic-expression.mdx b/docs/api/qiskit-addon-opt-mapper/problems-quadratic-expression.mdx new file mode 100644 index 00000000000..3559a2c4434 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-quadratic-expression.mdx @@ -0,0 +1,171 @@ +--- +title: QuadraticExpression (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.QuadraticExpression in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.QuadraticExpression +--- + + + +# qiskit\_addon\_opt\_mapper.problems.QuadraticExpression + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Representation of a quadratic expression by its coefficients. + + Creates a new quadratic expression. + + The quadratic expression can be defined via an array, a list, a sparse matrix, or a dictionary that uses variable names or indices as keys and stores the values internally as a dok\_matrix. We stores values in a compressed way, i.e., values at symmetric positions are summed up in the upper triangle. For example, \{(0, 1): 1, (1, 0): 2} -> \{(0, 1): 3}. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **coefficients** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The (sparse) representation of the coefficients. + + ### \_\_init\_\_ + + + Creates a new quadratic expression. + + The quadratic expression can be defined via an array, a list, a sparse matrix, or a dictionary that uses variable names or indices as keys and stores the values internally as a dok\_matrix. We stores values in a compressed way, i.e., values at symmetric positions are summed up in the upper triangle. For example, \{(0, 1): 1, (1, 0): 2} -> \{(0, 1): 3}. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **coefficients** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| spmatrix |*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*]] |* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*],* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The (sparse) representation of the coefficients. + + **Return type** + + None + + + ## Methods + + | | | + | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.__init__ "qiskit_addon_opt_mapper.problems.QuadraticExpression.__init__")(optimization\_problem, coefficients) | Creates a new quadratic expression. | + | [`evaluate`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.evaluate "qiskit_addon_opt_mapper.problems.QuadraticExpression.evaluate")(x) | Evaluate the quadratic expression for given variables: x \* Q \* x. | + | [`evaluate_gradient`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.evaluate_gradient "qiskit_addon_opt_mapper.problems.QuadraticExpression.evaluate_gradient")(x) | Evaluate the gradient of the quadratic expression for given variables. | + | [`to_array`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.to_array "qiskit_addon_opt_mapper.problems.QuadraticExpression.to_array")(\[symmetric]) | Returns the coefficients of the quadratic expression as array. | + | [`to_dict`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.to_dict "qiskit_addon_opt_mapper.problems.QuadraticExpression.to_dict")(\[symmetric, use\_name]) | Returns the coefficients of the quadratic expression as dictionary. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | + | [`bounds`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.bounds "qiskit_addon_opt_mapper.problems.QuadraticExpression.bounds") | Returns the lower bound and the upper bound of the quadratic expression. | + | [`coefficients`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.coefficients "qiskit_addon_opt_mapper.problems.QuadraticExpression.coefficients") | Returns the coefficients of the quadratic expression. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.QuadraticExpression.optimization_problem "qiskit_addon_opt_mapper.problems.QuadraticExpression.optimization_problem") | Returns the parent OptimizationProblem. | + + ### bounds + + + Returns the lower bound and the upper bound of the quadratic expression. + + **Returns** + + The lower bound and the upper bound of the quadratic expression + + **Raises** + + **OptimizationError** – if the quadratic expression contains any unbounded variable + + + ### coefficients + + + Returns the coefficients of the quadratic expression. + + **Returns** + + The coefficients of the quadratic expression. + + + ### evaluate + + + Evaluate the quadratic expression for given variables: x \* Q \* x. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the quadratic expression given the variable values. + + **Return type** + + [float](https://docs.python.org/3/library/functions.html#float) + + + ### evaluate\_gradient + + + Evaluate the gradient of the quadratic expression for given variables. + + **Parameters** + + **x** ([*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *|*[*list*](https://docs.python.org/3/library/stdtypes.html#list) *|*[*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int)*,* [*float*](https://docs.python.org/3/library/functions.html#float)*]*) – The values of the variables to be evaluated. + + **Returns** + + The value of the gradient quadratic expression given the variable values. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### to\_array + + + Returns the coefficients of the quadratic expression as array. + + **Parameters** + + **symmetric** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Determines whether the output is in a symmetric form or not. + + **Returns** + + An array with the coefficients corresponding to the quadratic expression. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### to\_dict + + + Returns the coefficients of the quadratic expression as dictionary. + + Either using tuples of variable names or indices as keys. + + **Parameters** + + * **symmetric** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Determines whether the output is in a symmetric form or not. + * **use\_name** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Determines whether to use index or names to refer to variables. + + **Returns** + + An dictionary with the coefficients corresponding to the quadratic expression. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), [int](https://docs.python.org/3/library/functions.html#int)] | [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [str](https://docs.python.org/3/library/stdtypes.html#str)], [float](https://docs.python.org/3/library/functions.html#float)] + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems-variable.mdx b/docs/api/qiskit-addon-opt-mapper/problems-variable.mdx new file mode 100644 index 00000000000..861d328066b --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems-variable.mdx @@ -0,0 +1,145 @@ +--- +title: Variable (latest version) +description: API reference for qiskit_addon_opt_mapper.problems.Variable in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.problems.Variable +--- + + + +# qiskit\_addon\_opt\_mapper.problems.Variable + + + Bases: [`OptimizationProblemElement`](problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.optimization_problem_element.OptimizationProblemElement") + + Representation of a variable. + + Creates a new Variable. + + The variables is exposed by the top-level OptimizationProblem class in OptimizationProblem.variables. This constructor is not meant to be used externally. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The variable name. + * **lowerbound** ([*float*](https://docs.python.org/3/library/functions.html#float) *|*[*int*](https://docs.python.org/3/library/functions.html#int)) – The variable lowerbound. + * **upperbound** ([*float*](https://docs.python.org/3/library/functions.html#float) *|*[*int*](https://docs.python.org/3/library/functions.html#int)) – The variable upperbound. + * **vartype** (*VarType*) – The variable type. + + **Raises** + + **OptimizationError** – if lowerbound is greater than upperbound. + + ### \_\_init\_\_ + + + Creates a new Variable. + + The variables is exposed by the top-level OptimizationProblem class in OptimizationProblem.variables. This constructor is not meant to be used externally. + + **Parameters** + + * **optimization\_problem** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any)) – The parent OptimizationProblem. + * **name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – The variable name. + * **lowerbound** ([*float*](https://docs.python.org/3/library/functions.html#float) *|*[*int*](https://docs.python.org/3/library/functions.html#int)) – The variable lowerbound. + * **upperbound** ([*float*](https://docs.python.org/3/library/functions.html#float) *|*[*int*](https://docs.python.org/3/library/functions.html#int)) – The variable upperbound. + * **vartype** (*VarType*) – The variable type. + + **Raises** + + **OptimizationError** – if lowerbound is greater than upperbound. + + **Return type** + + None + + + ## Methods + + | | | + | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.problems.Variable.__init__ "qiskit_addon_opt_mapper.problems.Variable.__init__")(optimization\_problem, name\[, ...]) | Creates a new Variable. | + | [`as_tuple`](#qiskit_addon_opt_mapper.problems.Variable.as_tuple "qiskit_addon_opt_mapper.problems.Variable.as_tuple")() | Returns a tuple corresponding to this variable. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | + | [`lowerbound`](#qiskit_addon_opt_mapper.problems.Variable.lowerbound "qiskit_addon_opt_mapper.problems.Variable.lowerbound") | Returns the lowerbound of the variable. | + | [`name`](#qiskit_addon_opt_mapper.problems.Variable.name "qiskit_addon_opt_mapper.problems.Variable.name") | Returns the name of the variable. | + | [`optimization_problem`](#qiskit_addon_opt_mapper.problems.Variable.optimization_problem "qiskit_addon_opt_mapper.problems.Variable.optimization_problem") | Returns the parent OptimizationProblem. | + | [`upperbound`](#qiskit_addon_opt_mapper.problems.Variable.upperbound "qiskit_addon_opt_mapper.problems.Variable.upperbound") | Returns the upperbound of the variable. | + | [`vartype`](#qiskit_addon_opt_mapper.problems.Variable.vartype "qiskit_addon_opt_mapper.problems.Variable.vartype") | Returns the type of the variable. | + + ### Type + + + alias of `VarType` + + + ### as\_tuple + + + Returns a tuple corresponding to this variable. + + **Returns** + + A tuple corresponding to this variable consisting of name, lowerbound, upperbound and variable type. + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [float](https://docs.python.org/3/library/functions.html#float) | [int](https://docs.python.org/3/library/functions.html#int), [float](https://docs.python.org/3/library/functions.html#float) | [int](https://docs.python.org/3/library/functions.html#int), *VarType*] + + + ### lowerbound + + + Returns the lowerbound of the variable. + + **Returns** + + The lower bound of the variable. + + + ### name + + + Returns the name of the variable. + + **Returns** + + The name of the variable. + + + ### optimization\_problem + + + Returns the parent OptimizationProblem. + + **Returns** + + The parent OptimizationProblem. + + + ### upperbound + + + Returns the upperbound of the variable. + + **Returns** + + The upperbound of the variable. + + + ### vartype + + + Returns the type of the variable. + + **Returns** + + The variable type. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/problems.mdx b/docs/api/qiskit-addon-opt-mapper/problems.mdx new file mode 100644 index 00000000000..dee45884795 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/problems.mdx @@ -0,0 +1,46 @@ +--- +title: problems (latest version) +description: API reference for qiskit_addon_opt_mapper.problems in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_opt_mapper.problems +--- + + + + + +# Problem Modeling Tools + +`qiskit_addon_opt_mapper.problems` + +Optimization problem model elements. + + + +## Optimization problems . + +`qiskit_addon_opt_mapper.problems` + +### Optimization problem + +Structures for defining an optimization problem. + + + The following classes are not intended to be instantiated directly. Objects of these types are available within an instantiated `OptimizationProblem`. + + +| | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| [`Constraint`](/docs/api/qiskit-addon-opt-mapper/problems-constraint "qiskit_addon_opt_mapper.problems.Constraint") | Abstract Constraint Class. | +| [`LinearExpression`](/docs/api/qiskit-addon-opt-mapper/problems-linear-expression "qiskit_addon_opt_mapper.problems.LinearExpression") | Representation of a linear expression by its coefficients. | +| [`LinearConstraint`](/docs/api/qiskit-addon-opt-mapper/problems-linear-constraint "qiskit_addon_opt_mapper.problems.LinearConstraint") | Representation of a linear constraint. | +| [`QuadraticExpression`](/docs/api/qiskit-addon-opt-mapper/problems-quadratic-expression "qiskit_addon_opt_mapper.problems.QuadraticExpression") | Representation of a quadratic expression by its coefficients. | +| [`QuadraticConstraint`](/docs/api/qiskit-addon-opt-mapper/problems-quadratic-constraint "qiskit_addon_opt_mapper.problems.QuadraticConstraint") | Representation of a quadratic constraint. | +| [`OptimizationProblemElement`](/docs/api/qiskit-addon-opt-mapper/problems-optimization-problem-element "qiskit_addon_opt_mapper.problems.OptimizationProblemElement") | Interface class for all objects that have a parent OptimizationProblem. | +| [`Variable`](/docs/api/qiskit-addon-opt-mapper/problems-variable "qiskit_addon_opt_mapper.problems.Variable") | Representation of a variable. | +| [`HigherOrderExpression`](/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression "qiskit_addon_opt_mapper.problems.HigherOrderExpression") | Representation of a symmetric k-th order expression by its coefficients. | +| [`HigherOrderConstraint`](/docs/api/qiskit-addon-opt-mapper/problems-higher-order-constraint "qiskit_addon_opt_mapper.problems.HigherOrderConstraint") | Constraint in higher order form. | +| [`HigherOrderExpression`](/docs/api/qiskit-addon-opt-mapper/problems-higher-order-expression "qiskit_addon_opt_mapper.problems.HigherOrderExpression") | Representation of a symmetric k-th order expression by its coefficients. | +| [`OptimizationObjective`](/docs/api/qiskit-addon-opt-mapper/problems-optimization-objective "qiskit_addon_opt_mapper.problems.OptimizationObjective") | Optimization objective element. | + diff --git a/docs/api/qiskit-addon-opt-mapper/release-notes.mdx b/docs/api/qiskit-addon-opt-mapper/release-notes.mdx new file mode 100644 index 00000000000..beacf4f2d6b --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/release-notes.mdx @@ -0,0 +1,46 @@ +--- +title: Optimization mapper release notes +description: Changes made to Optimization mapper +in_page_toc_max_heading_level: 2 +--- + + + + + +# Optimization mapper release notes + + + + + +## Upcoming release (`main`) + + + +### New Features + +* Added `BinaryToLinearBinary` converter, that converts a binary problem into a constrained linear binary problem. + + + + + +## 0.1.0 + + + + + +### New Features + +* Introduced the Qiskit addon for optimization modeling, designed to support utility-scale workloads powered by Qiskit. This package enables the formulation and transformation of combinatorial optimization problems into quantum-ready formats. It supports modeling of QUBO and HUBO problems, with higher-order terms represented as polynomials or monomials, and facilitates the creation of Hamiltonians whose ground states correspond to optimal solutions. + + ## Core modules include: + + * **applications**: Provides model implementations for common optimization problems. + * **converters**: Converters facilitate transformations between different representations and interpretation of solutions. + * **problems**: Defines optimization problems including objective functions, constraints, and variable types. + * **solvers**: Implements interfaces for modelling validation using classical solvers. + * **translators**: Converts optimization problem into additional optimization models. + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers-cplex-solver.mdx b/docs/api/qiskit-addon-opt-mapper/solvers-cplex-solver.mdx new file mode 100644 index 00000000000..c81901fb419 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers-cplex-solver.mdx @@ -0,0 +1,154 @@ +--- +title: CplexSolver (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers.CplexSolver in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.solvers.CplexSolver +--- + + + +# qiskit\_addon\_opt\_mapper.solvers.CplexSolver + + + Bases: [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.solver.OptimizationSolver") + + The CPLEX optimizer wrapped as a [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). + + This class provides a wrapper for `cplex.Cplex` ([https://pypi.org/project/cplex/](https://pypi.org/project/cplex/)) to be used within the optimization module. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.solvers import CplexSolver + >>> problem = OptimizationProblem() + >>> # specify problem here, if cplex is installed + >>> optimizer = CplexSolver() if CplexSolver.is_cplex_installed() else None + >>> if optimizer: result = optimizer.solve(problem) + ``` + + Initializes the CplexSolver. + + **Parameters** + + * **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print CPLEX output or not. + * **cplex\_parameters** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*Any*](https://docs.python.org/3/library/typing.html#typing.Any)*] | None*) – The parameters for CPLEX. See [https://www.ibm.com/docs/en/icos/20.1.0?topic=cplex-parameters](https://www.ibm.com/docs/en/icos/20.1.0?topic=cplex-parameters) for details. + + ### \_\_init\_\_ + + + Initializes the CplexSolver. + + **Parameters** + + * **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print CPLEX output or not. + * **cplex\_parameters** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*Any*](https://docs.python.org/3/library/typing.html#typing.Any)*] | None*) – The parameters for CPLEX. See [https://www.ibm.com/docs/en/icos/20.1.0?topic=cplex-parameters](https://www.ibm.com/docs/en/icos/20.1.0?topic=cplex-parameters) for details. + + **Return type** + + None + + + ## Methods + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.solvers.CplexSolver.__init__ "qiskit_addon_opt_mapper.solvers.CplexSolver.__init__")(\[disp, cplex\_parameters]) | Initializes the CplexSolver. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.solvers.CplexSolver.get_compatibility_msg "qiskit_addon_opt_mapper.solvers.CplexSolver.get_compatibility_msg")(problem) | Checks whether a given problem can be solved with this optimizer. | + | [`is_compatible`](#qiskit_addon_opt_mapper.solvers.CplexSolver.is_compatible "qiskit_addon_opt_mapper.solvers.CplexSolver.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + | [`is_cplex_installed`](#qiskit_addon_opt_mapper.solvers.CplexSolver.is_cplex_installed "qiskit_addon_opt_mapper.solvers.CplexSolver.is_cplex_installed")() | Returns True if cplex is installed. | + | [`solve`](#qiskit_addon_opt_mapper.solvers.CplexSolver.solve "qiskit_addon_opt_mapper.solvers.CplexSolver.solve")(problem) | Tries to solves the given problem using the optimizer. | + + ## Attributes + + | | | + | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | + | [`cplex_parameters`](#qiskit_addon_opt_mapper.solvers.CplexSolver.cplex_parameters "qiskit_addon_opt_mapper.solvers.CplexSolver.cplex_parameters") | Returns parameters for CPLEX. | + | [`disp`](#qiskit_addon_opt_mapper.solvers.CplexSolver.disp "qiskit_addon_opt_mapper.solvers.CplexSolver.disp") | Returns the display setting. | + + ### cplex\_parameters + + + Returns parameters for CPLEX. + + + ### disp + + + Returns the display setting. + + **Returns** + + Whether to print CPLEX information or not. + + + ### get\_compatibility\_msg + + + Checks whether a given problem can be solved with this optimizer. + + Returns `''` since CPLEX accepts all problems that can be modeled using the `OptimizationProblem`. CPLEX may throw an exception in case the problem is determined to be non-convex. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + An empty string. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### is\_cplex\_installed + + + Returns True if cplex is installed. + + + ### solve + + + Tries to solves the given problem using the optimizer. + + Runs the optimizer to try to solve the optimization problem. If problem is not convex, this optimizer may raise an exception due to incompatibility, depending on the settings. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved. + + **Returns** + + The result of the optimizer applied to the problem. + + **Raises** + + **QiskitOptimizationError** – If the problem is incompatible with the optimizer. + + **Return type** + + [*SolverResult*](solvers-solver-result "qiskit_addon_opt_mapper.solvers.solver.SolverResult") + + + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers-gurobi-solver.mdx b/docs/api/qiskit-addon-opt-mapper/solvers-gurobi-solver.mdx new file mode 100644 index 00000000000..db01ae1fcb1 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers-gurobi-solver.mdx @@ -0,0 +1,151 @@ +--- +title: GurobiSolver (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers.GurobiSolver in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.solvers.GurobiSolver +--- + + + +# qiskit\_addon\_opt\_mapper.solvers.GurobiSolver + + + Bases: [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.solver.OptimizationSolver") + + The Gurobi optimizer wrapped as a [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). + + This class provides a wrapper for `gurobipy` to be used within the optimization module. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper.problems import OptimizationProblem + >>> from qiskit_addon_opt_mapper.solvers import GurobiSolver + >>> problem = OptimizationProblem() + >>> # specify problem here, if gurobi is installed + >>> optimizer = GurobiSolver() if GurobiSolver.is_gurobi_installed() else None + >>> # Suppress gurobipy print info to stdout + >>> import sys + >>> class DevNull: + ... def noop(*args, **kwargs): pass + ... close = write = flush = writelines = noop + >>> sys.stdout = DevNull() + >>> result = optimizer.solve(problem) + ``` + + Initializes the GurobiSolver. + + **Parameters** + + **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print Gurobi output or not. + + ### \_\_init\_\_ + + + Initializes the GurobiSolver. + + **Parameters** + + **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print Gurobi output or not. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.__init__ "qiskit_addon_opt_mapper.solvers.GurobiSolver.__init__")(\[disp]) | Initializes the GurobiSolver. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.get_compatibility_msg "qiskit_addon_opt_mapper.solvers.GurobiSolver.get_compatibility_msg")(problem) | Checks whether a given problem can be solved with this optimizer. | + | [`is_compatible`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.is_compatible "qiskit_addon_opt_mapper.solvers.GurobiSolver.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + | [`is_gurobi_installed`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.is_gurobi_installed "qiskit_addon_opt_mapper.solvers.GurobiSolver.is_gurobi_installed")() | Returns True if gurobi is installed. | + | [`solve`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.solve "qiskit_addon_opt_mapper.solvers.GurobiSolver.solve")(problem) | Tries to solves the given problem using the optimizer. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------- | ---------------------------- | + | [`disp`](#qiskit_addon_opt_mapper.solvers.GurobiSolver.disp "qiskit_addon_opt_mapper.solvers.GurobiSolver.disp") | Returns the display setting. | + + ### disp + + + Returns the display setting. + + **Returns** + + Whether to print Gurobi information or not. + + + ### get\_compatibility\_msg + + + Checks whether a given problem can be solved with this optimizer. + + Returns `''` since Gurobi accepts all problems that can be modeled using the `OptimizationProblem`. Gurobi will also solve non-convex problems. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + An empty string. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### is\_gurobi\_installed + + + Returns True if gurobi is installed. + + + ### solve + + + Tries to solves the given problem using the optimizer. + + Runs the optimizer to try to solve the optimization problem. If problem is not convex, this optimizer may raise an exception due to incompatibility, depending on the settings. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved. + + **Returns** + + The result of the optimizer applied to the problem. + + **Raises** + + **OptimizationError** – If the problem is incompatible with the optimizer. + + **Return type** + + [*SolverResult*](solvers-solver-result "qiskit_addon_opt_mapper.solvers.solver.SolverResult") + + + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver.mdx b/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver.mdx new file mode 100644 index 00000000000..98f62ccbe65 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver.mdx @@ -0,0 +1,91 @@ +--- +title: OptimizationSolver (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers.OptimizationSolver in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.solvers.OptimizationSolver +--- + + + +# qiskit\_addon\_opt\_mapper.solvers.OptimizationSolver + + + Bases: [`ABC`](https://docs.python.org/3/library/abc.html#abc.ABC) + + An abstract class for optimization solvers in the qiskit addon opt mapper. + + ### \_\_init\_\_ + + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.solvers.OptimizationSolver.__init__ "qiskit_addon_opt_mapper.solvers.OptimizationSolver.__init__")() | | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.solvers.OptimizationSolver.get_compatibility_msg "qiskit_addon_opt_mapper.solvers.OptimizationSolver.get_compatibility_msg")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + | [`is_compatible`](#qiskit_addon_opt_mapper.solvers.OptimizationSolver.is_compatible "qiskit_addon_opt_mapper.solvers.OptimizationSolver.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + | [`solve`](#qiskit_addon_opt_mapper.solvers.OptimizationSolver.solve "qiskit_addon_opt_mapper.solvers.OptimizationSolver.solve")(problem) | Tries to solves the given problem using the optimizer. | + + ### get\_compatibility\_msg + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns the incompatibility message. If the message is empty no issues were found. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### solve + + + Tries to solves the given problem using the optimizer. + + Runs the optimizer to try to solve the optimization problem. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved. + + **Returns** + + The result of the optimizer applied to the problem. + + **Raises** + + **OptimizationError** – If the problem is incompatible with the optimizer. + + **Return type** + + [*SolverResult*](solvers-solver-result "qiskit_addon_opt_mapper.solvers.solver.SolverResult") + + + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers-scipy-milp-solver.mdx b/docs/api/qiskit-addon-opt-mapper/solvers-scipy-milp-solver.mdx new file mode 100644 index 00000000000..52f18da42ed --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers-scipy-milp-solver.mdx @@ -0,0 +1,127 @@ +--- +title: ScipyMilpSolver (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers.ScipyMilpSolver in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.solvers.ScipyMilpSolver +--- + + + +# qiskit\_addon\_opt\_mapper.solvers.ScipyMilpSolver + + + Bases: [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.solver.OptimizationSolver") + + The MILP optimizer from Scipy wrapped as a Qiskit [`OptimizationSolver`](solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). + + This class provides a wrapper for `scipy.milp` ([https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.milp.html](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.milp.html)) to be used within the optimization module. + + Initializes the ScipyMILPOptimizer. + + **Parameters** + + **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print MILP output or not. + + ### \_\_init\_\_ + + + Initializes the ScipyMILPOptimizer. + + **Parameters** + + **disp** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print MILP output or not. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`__init__`](#qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.__init__ "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.__init__")(\[disp]) | Initializes the ScipyMILPOptimizer. | + | [`get_compatibility_msg`](#qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.get_compatibility_msg "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.get_compatibility_msg")(problem) | Checks whether a given problem can be solved with this optimizer. | + | [`is_compatible`](#qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.is_compatible "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.is_compatible")(problem) | Checks whether a given problem can be solved with the optimizer implementing this method. | + | [`solve`](#qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.solve "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.solve")(problem) | Tries to solve the given problem using the optimizer. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------- | + | [`disp`](#qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.disp "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver.disp") | Returns the display setting. | + + ### disp + + + Returns the display setting. + + **Returns** + + Whether to print scipy.milp information or not. + + + ### get\_compatibility\_msg + + + Checks whether a given problem can be solved with this optimizer. + + Checks if the problem has only linear objective function and linear constraints. The `scipy.milp` supports only linear objective function and linear constraints. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + An empty string (if compatible) or a string describing the incompatibility. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### is\_compatible + + + Checks whether a given problem can be solved with the optimizer implementing this method. + + **Parameters** + + **problem** (*OptimizationProblem*) – The optimization problem to check compatibility. + + **Returns** + + Returns True if the problem is compatible, False otherwise. + + **Return type** + + [bool](https://docs.python.org/3/library/functions.html#bool) + + + ### solve + + + Tries to solve the given problem using the optimizer. + + Runs the optimizer to try to solve the optimization problem. If problem is not convex, this optimizer may raise an exception due to incompatibility, depending on the settings. + + **Parameters** + + **problem** (*OptimizationProblem*) – The problem to be solved. + + **Returns** + + The result of the optimizer applied to the problem. + + **Raises** + + **QiskitOptimizationError** – If the problem is incompatible with the optimizer. + + **Return type** + + [*SolverResult*](solvers-solver-result "qiskit_addon_opt_mapper.solvers.solver.SolverResult") + + + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers-solver-result.mdx b/docs/api/qiskit-addon-opt-mapper/solvers-solver-result.mdx new file mode 100644 index 00000000000..2386810c46f --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers-solver-result.mdx @@ -0,0 +1,223 @@ +--- +title: SolverResult (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers.SolverResult in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: class +python_api_name: qiskit_addon_opt_mapper.solvers.SolverResult +--- + + + +# qiskit\_addon\_opt\_mapper.solvers.SolverResult + + + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) + + A base class for optimization results. + + The optimization algorithms return an object of the type `SolverResult` with the information about the solution obtained. + + `SolverResult` allows users to get the value of a variable by specifying an index or a name as follows. + + **Examples** + + ```python + >>> from qiskit_addon_opt_mapper import OptimizationProblem + >>> from qiskit_addon_opt_mapper.solvers import CplexSolver + >>> problem = OptimizationProblem() + >>> _ = problem.binary_var('x1') + >>> _ = problem.binary_var('x2') + >>> _ = problem.binary_var('x3') + >>> problem.minimize(linear={'x1': 1, 'x2': -2, 'x3': 3}) + >>> print([var.name for var in problem.variables]) + ['x1', 'x2', 'x3'] + >>> optimizer = CplexSolver() + >>> result = optimizer.solve(problem) + >>> print(result.variable_names) + ['x1', 'x2', 'x3'] + >>> print(result.x) + [0. 1. 0.] + >>> print(result[1]) + 1.0 + >>> print(result['x1']) + 0.0 + >>> print(result.fval) + -2.0 + >>> print(result.variables_dict) + {'x1': 0.0, 'x2': 1.0, 'x3': 0.0} + ``` + + + The order of variables should be equal to that of the problem solved by optimization algorithms. Optimization algorithms and converters of `OptimizationProblem` should maintain the order when generating a new `SolverResult` object. + + + Init method. + + **Parameters** + + * **x** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] | np.ndarray | None*) – the variable values found in the optimization, or possibly None in case of FAILURE. + * **fval** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the objective function value. + * **variables** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*Variable*](problems-variable "qiskit_addon_opt_mapper.problems.Variable")*]*) – the list of variables of the optimization problem. + * **raw\_results** (*Any | None*) – the original results object from the optimization algorithm. + * **status** (*SolverResultStatus*) – the termination status of the optimization algorithm. + * **samples** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[SolutionSample] | None*) – the solution samples. + + **Raises** + + **OptimizationError** – if sizes of `x` and `variables` do not match or one of (fval, samples) is not provided. + + ### \_\_init\_\_ + + + Init method. + + **Parameters** + + * **x** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*float*](https://docs.python.org/3/library/functions.html#float)*] |* [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) *| None*) – the variable values found in the optimization, or possibly None in case of FAILURE. + * **fval** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the objective function value. + * **variables** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*Variable*](problems-variable "qiskit_addon_opt_mapper.problems.variable.Variable")*]*) – the list of variables of the optimization problem. + * **raw\_results** ([*Any*](https://docs.python.org/3/library/typing.html#typing.Any) *| None*) – the original results object from the optimization algorithm. + * **status** (*SolverResultStatus*) – the termination status of the optimization algorithm. + * **samples** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[SolutionSample] | None*) – the solution samples. + + **Raises** + + **OptimizationError** – if sizes of `x` and `variables` do not match or one of (fval, samples) is not provided. + + **Return type** + + None + + + ## Methods + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | + | [`__init__`](#qiskit_addon_opt_mapper.solvers.SolverResult.__init__ "qiskit_addon_opt_mapper.solvers.SolverResult.__init__")(x, fval, variables, status\[, ...]) | Init method. | + | [`get_correlations`](#qiskit_addon_opt_mapper.solvers.SolverResult.get_correlations "qiskit_addon_opt_mapper.solvers.SolverResult.get_correlations")() | Get \ correlation matrix from the samples. | + | [`prettyprint`](#qiskit_addon_opt_mapper.solvers.SolverResult.prettyprint "qiskit_addon_opt_mapper.solvers.SolverResult.prettyprint")() | Returns a pretty printed string of this optimization result. | + + ## Attributes + + | | | + | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | + | [`fval`](#qiskit_addon_opt_mapper.solvers.SolverResult.fval "qiskit_addon_opt_mapper.solvers.SolverResult.fval") | Returns the objective function value. | + | [`raw_results`](#qiskit_addon_opt_mapper.solvers.SolverResult.raw_results "qiskit_addon_opt_mapper.solvers.SolverResult.raw_results") | Return the original results object from the optimization algorithm. | + | [`samples`](#qiskit_addon_opt_mapper.solvers.SolverResult.samples "qiskit_addon_opt_mapper.solvers.SolverResult.samples") | Returns the list of solution samples. | + | [`status`](#qiskit_addon_opt_mapper.solvers.SolverResult.status "qiskit_addon_opt_mapper.solvers.SolverResult.status") | Returns the termination status of the optimization algorithm. | + | [`variable_names`](#qiskit_addon_opt_mapper.solvers.SolverResult.variable_names "qiskit_addon_opt_mapper.solvers.SolverResult.variable_names") | Returns the list of variable names of the optimization problem. | + | [`variables`](#qiskit_addon_opt_mapper.solvers.SolverResult.variables "qiskit_addon_opt_mapper.solvers.SolverResult.variables") | Returns the list of variables of the optimization problem. | + | [`variables_dict`](#qiskit_addon_opt_mapper.solvers.SolverResult.variables_dict "qiskit_addon_opt_mapper.solvers.SolverResult.variables_dict") | Returns the variable values as a dictionary of the variable name and corresponding value. | + | [`x`](#qiskit_addon_opt_mapper.solvers.SolverResult.x "qiskit_addon_opt_mapper.solvers.SolverResult.x") | Returns the variable values found in the optimization or None in case of FAILURE. | + + ### fval + + + Returns the objective function value. + + **Returns** + + The function value corresponding to the objective function value found in the optimization. + + + ### get\_correlations + + + Get \ correlation matrix from the samples. + + **Returns** + + A correlation matrix. + + **Return type** + + [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray) + + + ### prettyprint + + + Returns a pretty printed string of this optimization result. + + **Returns** + + A pretty printed string representing the result. + + **Return type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) + + + ### raw\_results + + + Return the original results object from the optimization algorithm. + + Currently a dump for any leftovers. + + **Returns** + + Additional result information of the optimization algorithm. + + + ### samples + + + Returns the list of solution samples. + + **Returns** + + The list of solution samples. + + + ### status + + + Returns the termination status of the optimization algorithm. + + **Returns** + + The termination status of the algorithm. + + + ### variable\_names + + + Returns the list of variable names of the optimization problem. + + **Returns** + + The list of variable names of the optimization problem. + + + ### variables + + + Returns the list of variables of the optimization problem. + + **Returns** + + The list of variables. + + + ### variables\_dict + + + Returns the variable values as a dictionary of the variable name and corresponding value. + + **Returns** + + The variable values as a dictionary of the variable name and corresponding value. + + + ### x + + + Returns the variable values found in the optimization or None in case of FAILURE. + + **Returns** + + The variable values found in the optimization. + + + diff --git a/docs/api/qiskit-addon-opt-mapper/solvers.mdx b/docs/api/qiskit-addon-opt-mapper/solvers.mdx new file mode 100644 index 00000000000..b035f2d6701 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/solvers.mdx @@ -0,0 +1,39 @@ +--- +title: solvers (latest version) +description: API reference for qiskit_addon_opt_mapper.solvers in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_opt_mapper.solvers +--- + + + + + +# Reference Solvers + +`qiskit_addon_opt_mapper.solvers` + +Solvers module. + + + +## Classical Solvers . + +`qiskit_addon_opt_mapper.solvers` + +### Base class for solvers and results + +| | | +| ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | +| [`OptimizationSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver") | An abstract class for optimization solvers in the qiskit addon opt mapper. | +| [`SolverResult`](/docs/api/qiskit-addon-opt-mapper/solvers-solver-result "qiskit_addon_opt_mapper.solvers.SolverResult") | A base class for optimization results. | + +### Classical Solvers + +| | | +| ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`CplexSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-cplex-solver "qiskit_addon_opt_mapper.solvers.CplexSolver") | The CPLEX optimizer wrapped as a [`OptimizationSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). | +| [`GurobiSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-gurobi-solver "qiskit_addon_opt_mapper.solvers.GurobiSolver") | The Gurobi optimizer wrapped as a [`OptimizationSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). | +| [`ScipyMilpSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-scipy-milp-solver "qiskit_addon_opt_mapper.solvers.ScipyMilpSolver") | The MILP optimizer from Scipy wrapped as a Qiskit [`OptimizationSolver`](/docs/api/qiskit-addon-opt-mapper/solvers-optimization-solver "qiskit_addon_opt_mapper.solvers.OptimizationSolver"). | + diff --git a/docs/api/qiskit-addon-opt-mapper/translators-from-docplex-mp.mdx b/docs/api/qiskit-addon-opt-mapper/translators-from-docplex-mp.mdx new file mode 100644 index 00000000000..5d5f0059069 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/translators-from-docplex-mp.mdx @@ -0,0 +1,43 @@ +--- +title: from_docplex_mp (latest version) +description: API reference for qiskit_addon_opt_mapper.translators.from_docplex_mp in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: qiskit_addon_opt_mapper.translators.from_docplex_mp +--- + + + +# qiskit\_addon\_opt\_mapper.translators.from\_docplex\_mp + + + Translate a docplex.mp model into a quadratic problem. + + Note that this supports the following features of docplex: + + * linear / quadratic objective function + * linear / quadratic / indicator constraints + * binary / integer / continuous variables + * logical expressions (`logical_not`, `logical_and`, and `logical_or`) + + Higher-order objective terms and constraints are not supported in a docplex.mp model. Also spin variables are not supported. + + **Parameters** + + * **model** (*Model*) – The docplex.mp model to be loaded. + * **indicator\_big\_m** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – The big-M value used for the big-M formulation to convert indicator constraints into linear constraints. If `None`, it is automatically derived from the model. + + **Returns** + + The quadratic problem corresponding to the model. + + **Raises** + + * [**RuntimeError**](https://docs.python.org/3/library/exceptions.html#RuntimeError) – if docplex is not installed. + * **OptimizationError** – if the model contains unsupported elements. + + **Return type** + + *OptimizationProblem* + + diff --git a/docs/api/qiskit-addon-opt-mapper/translators-to-docplex-mp.mdx b/docs/api/qiskit-addon-opt-mapper/translators-to-docplex-mp.mdx new file mode 100644 index 00000000000..ed33f7627c6 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/translators-to-docplex-mp.mdx @@ -0,0 +1,39 @@ +--- +title: to_docplex_mp (latest version) +description: API reference for qiskit_addon_opt_mapper.translators.to_docplex_mp in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: qiskit_addon_opt_mapper.translators.to_docplex_mp +--- + + + +# qiskit\_addon\_opt\_mapper.translators.to\_docplex\_mp + + + Returns a docplex.mp model corresponding to a optimization problem. + + Higher-order terms and spin variables are not supported. + + **Parameters** + + **quadratic\_problem** (*OptimizationProblem*) – The optimization problem to be translated. + + **Returns** + + The docplex.mp model corresponding to a quadratic optimization problem. + + **Raises** + + * [**RuntimeError**](https://docs.python.org/3/library/exceptions.html#RuntimeError) – if docplex is not installed. + * **OptimizationError** – if the model contains spin variables. + * **OptimizationError** – if the optimization problem contains unsupported variables. (should + * **never happen**\*\*)\*\* – + * **OptimizationError** – if the optimization problem contains higher-order objective terms. + * **OptimizationError** – if the optimization problem contains higher-order constraints. + + **Return type** + + *Model* + + diff --git a/docs/api/qiskit-addon-opt-mapper/translators-to-ising.mdx b/docs/api/qiskit-addon-opt-mapper/translators-to-ising.mdx new file mode 100644 index 00000000000..9b847a13ccb --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/translators-to-ising.mdx @@ -0,0 +1,35 @@ +--- +title: to_ising (latest version) +description: API reference for qiskit_addon_opt_mapper.translators.to_ising in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 1 +python_api_type: function +python_api_name: qiskit_addon_opt_mapper.translators.to_ising +--- + + + +# qiskit\_addon\_opt\_mapper.translators.to\_ising + + + Return the Ising Hamiltonian of this problem. + + Variables are mapped to qubits in qiskit order, i.e., i-th variable is mapped to index n-i in the pauli string, where n is the total number of variables. + + **Parameters** + + **optimization\_problem** (*OptimizationProblem*) – The problem to be translated. + + **Returns** + + A tuple (qubit\_op, offset) comprising the qubit operator for the problem and offset for the constant value in the Ising Hamiltonian. + + **Raises** + + * **OptimizationError** – If an integer variable or a continuous variable exists in the problem. + * **OptimizationError** – If constraints exist in the problem. + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp), [float](https://docs.python.org/3/library/functions.html#float)] + + diff --git a/docs/api/qiskit-addon-opt-mapper/translators.mdx b/docs/api/qiskit-addon-opt-mapper/translators.mdx new file mode 100644 index 00000000000..7707063dc55 --- /dev/null +++ b/docs/api/qiskit-addon-opt-mapper/translators.mdx @@ -0,0 +1,34 @@ +--- +title: translators (latest version) +description: API reference for qiskit_addon_opt_mapper.translators in the latest version of qiskit-addon-opt-mapper +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_opt_mapper.translators +--- + + + + + +# Translators + +`qiskit_addon_opt_mapper.translators` + +Translators. + + + +## Optimization problem translators . + +`qiskit_addon_opt_mapper.translators` + +Translators between `OptimizationProblem` and other optimization models or other objects. + +### Translators + +| | | +| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [`from_docplex_mp`](/docs/api/qiskit-addon-opt-mapper/translators-from-docplex-mp "qiskit_addon_opt_mapper.translators.from_docplex_mp") | Translate a docplex.mp model into a quadratic problem. | +| [`to_docplex_mp`](/docs/api/qiskit-addon-opt-mapper/translators-to-docplex-mp "qiskit_addon_opt_mapper.translators.to_docplex_mp") | Returns a docplex.mp model corresponding to a optimization problem. | +| [`to_ising`](/docs/api/qiskit-addon-opt-mapper/translators-to-ising "qiskit_addon_opt_mapper.translators.to_ising") | Return the Ising Hamiltonian of this problem. | + diff --git a/docs/api/qiskit-addon-pna/_package.json b/docs/api/qiskit-addon-pna/_package.json new file mode 100644 index 00000000000..a5b2b7b671a --- /dev/null +++ b/docs/api/qiskit-addon-pna/_package.json @@ -0,0 +1,4 @@ +{ + "name": "qiskit-addon-pna", + "version": "0.2.0" +} diff --git a/docs/api/qiskit-addon-pna/_toc.json b/docs/api/qiskit-addon-pna/_toc.json new file mode 100644 index 00000000000..98530103316 --- /dev/null +++ b/docs/api/qiskit-addon-pna/_toc.json @@ -0,0 +1,23 @@ +{ + "title": "Propagated noise absorption (PNA)", + "children": [ + { + "title": "API index", + "url": "/docs/api/qiskit-addon-pna" + }, + { + "title": "Release notes", + "useDivider": true, + "url": "/docs/api/qiskit-addon-pna/release-notes" + }, + { + "title": "qiskit_addon_pna", + "url": "/docs/api/qiskit-addon-pna/qiskit-addon-pna", + "untranslatable": true + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-pna", + "parentLabel": "Propagated noise absorption (PNA)" +} diff --git a/docs/api/qiskit-addon-pna/index.mdx b/docs/api/qiskit-addon-pna/index.mdx new file mode 100644 index 00000000000..32dd5c7438b --- /dev/null +++ b/docs/api/qiskit-addon-pna/index.mdx @@ -0,0 +1,11 @@ +--- +title: Propagated noise absorption (PNA) API documentation (latest version) +description: Index of all the modules in the latest version of qiskit-addon-pna. +--- + + + +# `qiskit-addon-pna` API reference + +* [Propagated Noise Absorption (`qiskit_addon_pna`)](qiskit-addon-pna) + diff --git a/docs/api/qiskit-addon-pna/qiskit-addon-pna.mdx b/docs/api/qiskit-addon-pna/qiskit-addon-pna.mdx new file mode 100644 index 00000000000..bab4a4ba8e5 --- /dev/null +++ b/docs/api/qiskit-addon-pna/qiskit-addon-pna.mdx @@ -0,0 +1,84 @@ +--- +title: qiskit_addon_pna (latest version) +description: API reference for qiskit_addon_pna in the latest version of qiskit-addon-pna +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_pna +--- + + + + + +# Propagated Noise Absorption + +`qiskit_addon_pna` + +Primary propagated noise absortption (PNA) functionality. + +### generate\_noise\_mitigating\_observable + + + Generate a noise-mitigating observable by propagating it through the inverse of a learned noise channel. + + + This function uses the Python `multiprocessing` module for parallel execution. This function should be called from within an `if __name__ == "__main__"` guard to prevent unintended process spawning. + + + Starting from the beginning of the circuit, the noise affecting each entangling layer is inverted and each Pauli anti-noise generator is then propagated forward through the remainder of the circuit and applied to the observable. The propagation routines used to implement this method are available in the [pauli-prop](https://qiskit.github.io/pauli-prop/) package. + + As each anti-noise generator is propagated forward through the circuit under the action of $N$ Pauli rotation gates of an \$M\$-qubit circuit, the number of terms will grow as $O(2^N)$ towards a maximum of $4^M$ unique Pauli components. To control the computational cost, terms with small coefficients must be truncated, which will result in some error in the evolved anti-noise channel. + + In addition to the truncation of the evolved anti-noise channel, $\Lambda^{-1}$, $\tilde{O}$ is also truncated as it is propagated through $\Lambda^{-1}$. This is also a source of bias in the final mitigated expectation value. + + While letting $\tilde{O}$ grow larger during propagation will increase its accuracy, measuring it requires taking many more shots on the QPU. Typically this increases the coefficients of the original Pauli terms in $O$, along with creating many new Pauli terms with smaller coefficients. Both the rescaling of the original coefficients and the creation of new terms can increase sampling overhead. In practice, we truncate once more by measuring only the largest terms in $\tilde{O}$. + + **Parameters** + + * **noisy\_circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – + + A circuit with associated Pauli-Lindblad gate noise. + + If this circuit is boxed, there are expected to be `InjectNoise` annotations associated with each box for which gate noise should be mitigated. Additionally, the `refs_to_noise_model_map` should provide a mapping from the reference ID of each `InjectNoise` annotation to the associated noise model for that layer, specified as a `PauliLindbladMap`. + + If this circuit is not boxed, the noise model is expected to be embedded as `PauliLindbladError` instructions adjacent to each circuit layer for which gate noise should be mitigated. In this case, `refs_to_noise_model_map` may be None. + + * **observable** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) *|*[*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli)) – The observable which will absorb the anti-noise. + + * **refs\_to\_noise\_model\_map** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*] | None*) – A dictionary mapping noise injection referencs IDs to their corresponding noise models as `PauliLindbladMap`. If `noisy_circuit` is not boxed and contains `PauliLindbladError` instructions from qiskit-aer, this mapping is not needed. + + * **max\_err\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum number of terms each anti-noise generator may contain as it evolves through the circuit + + * **max\_obs\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The maximum number of terms the noise-mitigating observable may contain + + * **search\_step** ([*int*](https://docs.python.org/3/library/functions.html#int)) – A parameter that can speed up the approximate application of each error to the observable. The relevant subroutine searches a very large 3D space to identify the `max_obs_terms` largest terms in a product. Setting this step size >1 accelerates that search by a factor of `search_step**3`, at a potential cost in accuracy. This inaccuracy is expected to be small for `search_step**3 << max_obs_terms`. + + * **num\_processes** ([*int*](https://docs.python.org/3/library/functions.html#int)) – The number of processes for parallelization. These may be used for forward evolution of generators, and for applying evolved generators to the observable. If `batch_size` is `1` (default), all are used for evolving generators. Otherwise, `max(min(batch_size, num_processes // 2), 1)` of these will be allocated for applying evolved generators to the observable. + + * **print\_progress** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – Whether to print progress to stdout + + * **atol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – Terms below this threshold will not be added to operators as they evolve + + * **batch\_size** ([*int*](https://docs.python.org/3/library/functions.html#int)) – Setting this to a value > 1 allows batches of noise generators to be applied to the observable in parallel. This coarse-grain application of anti-noise to the observable comes at a loss of accuracy related to the probability that more than one error in the batch occurs when the circuit is run. This should usually not be set higher than `max(1, num_processes // 2)`. + + * **inject\_noise\_before** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – If `True`, the Pauli Lindblad noise instruction will be inserted before its corresponding 2q-gate layer. Otherwise, it will be inserted after it, defaults to `True`. + + * **mp\_start\_method** ([*str*](https://docs.python.org/3/library/stdtypes.html#str) *| None*) – The method to use when starting new parallel processes. Valid values are `fork`, `spawn`, `forkserver`, and `None`. If `None`, the default method will be used. + + **Returns** + + The noise-mitigating observable + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – The circuit and observable have mismatching sizes + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – num\_processes and batch\_size must be >= 1 + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – `max_obs_terms` should be larger than the length of `observable` + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Incompatible noisy circuit and refs\_to\_noise\_model\_map + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – The observable must only contain real-valued coefficients + + **Return type** + + [*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) + + diff --git a/docs/api/qiskit-addon-pna/release-notes.mdx b/docs/api/qiskit-addon-pna/release-notes.mdx new file mode 100644 index 00000000000..36f63947363 --- /dev/null +++ b/docs/api/qiskit-addon-pna/release-notes.mdx @@ -0,0 +1,38 @@ +--- +title: Propagated noise absorption (PNA) release notes +description: Changes made to Propagated noise absorption (PNA) +in_page_toc_max_heading_level: 2 +--- + + + + + +# Propagated noise absorption (PNA) release notes + + + + + +## 0.2.0 + + + +### New Features + +* Added an `mp_start_method` kwarg to [`qiskit_addon_pna.generate_noise_mitigating_observable()`](/docs/api/qiskit-addon-pna/qiskit-addon-pna#generate_noise_mitigating_observable "qiskit_addon_pna.generate_noise_mitigating_observable") to specify the method to use to start new parallel processes. + + + + + +## 0.1.0 + + + + + +### New Features + +* Add argument to [`qiskit_addon_pna.generate_noise_mitigating_observable()`](/docs/api/qiskit-addon-pna/qiskit-addon-pna#generate_noise_mitigating_observable "qiskit_addon_pna.generate_noise_mitigating_observable") specifying whether noise should be injected before or after entangling layers. + diff --git a/docs/api/qiskit-addon-slc/_package.json b/docs/api/qiskit-addon-slc/_package.json new file mode 100644 index 00000000000..961052ce099 --- /dev/null +++ b/docs/api/qiskit-addon-slc/_package.json @@ -0,0 +1,4 @@ +{ + "name": "qiskit-addon-slc", + "version": "0.1.0" +} diff --git a/docs/api/qiskit-addon-slc/_toc.json b/docs/api/qiskit-addon-slc/_toc.json new file mode 100644 index 00000000000..acabed988b1 --- /dev/null +++ b/docs/api/qiskit-addon-slc/_toc.json @@ -0,0 +1,33 @@ +{ + "title": "Shaded lightcones", + "children": [ + { + "title": "API index", + "url": "/docs/api/qiskit-addon-slc" + }, + { + "title": "Release notes", + "useDivider": true, + "url": "/docs/api/qiskit-addon-slc/release-notes" + }, + { + "title": "qiskit_addon_slc.bounds", + "url": "/docs/api/qiskit-addon-slc/bounds", + "untranslatable": true + }, + { + "title": "qiskit_addon_slc.utils", + "url": "/docs/api/qiskit-addon-slc/utils", + "untranslatable": true + }, + { + "title": "qiskit_addon_slc.visualization", + "url": "/docs/api/qiskit-addon-slc/visualization", + "untranslatable": true + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-slc", + "parentLabel": "Shaded lightcones" +} diff --git a/docs/api/qiskit-addon-slc/bounds.mdx b/docs/api/qiskit-addon-slc/bounds.mdx new file mode 100644 index 00000000000..03a8be29939 --- /dev/null +++ b/docs/api/qiskit-addon-slc/bounds.mdx @@ -0,0 +1,183 @@ +--- +title: bounds (latest version) +description: API reference for qiskit_addon_slc.bounds in the latest version of qiskit-addon-slc +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_slc.bounds +--- + + + + + +# Bounds + +`qiskit_addon_slc.bounds` + +Bound computation functions. + +This module provides various functions for computing the error bounds that make up a shaded lightcone. + +### compute\_forward\_bounds + + + Compute the forward-evolved unequal-time commutator bounds. + + Starting at the end of the circuit, compute the forward-evolved unequal-time commutator bounds for all Pauli error terms of each noisy layer in the target circuit. + + That is, compute $\| \left[ E_F, A_F \right] \|_2$ for all error terms, $E_F$, where $A_F$ is the target `observable` to be measured on `circuit`. + + The error terms, $E_I$, are dictated by `noise_model_paulis`. This dictionary maps noise model identifiers (`samplomatic.InjectNoise.ref`) to a list of Pauli error terms. The corresponding terms will be used whenever a [`BoxOp`](/docs/api/qiskit/qiskit.circuit.BoxOp) with a matching [`InjectNoise`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.InjectNoise.html#samplomatic.InjectNoise "(in samplomatic)") annotation is encountered during the iteration over `circuit`. + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the target circuit. + * **noise\_model\_paulis** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)*]*) – the Pauli error terms to consider for each noise model. + * **observable** ([*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli) *|*[*PauliList*](/docs/api/qiskit/qiskit.quantum_info.PauliList) *|*[*SparseObservable*](/docs/api/qiskit/qiskit.quantum_info.SparseObservable) *|*[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – the target observable to be measured at the end of the circuit. + * **evolution\_max\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the maximum number of operator terms to keep track of during the evolution. + * **eigval\_max\_qubits** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the maximum number of qubits of a commutator for which the eigenvalue will still be attempted to be computed. When this value is exceeded, the bound is approximated via a simpler and more loose triangle inequality. + * **atol** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the absolute tolerance used for trimming terms from the commutator and for detecting convergence of the commutator’s eigenvalue. + * **num\_processes** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the number of parallel processes to use. + * **timeout** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – an optional timeout (in seconds) after which all remaining layers are filled with trivial numerical bounds of `2.0`. Note, that this is not a strict timeout and the layer being processed at the time of reaching this timeout will complete normally. + + **Returns** + + The unequal-time commutator bound. + + **Raises** + + [**NotImplementedError**](https://docs.python.org/3/library/exceptions.html#NotImplementedError) – when the `observable` contains more than a single Pauli term. If you run into this, you will need to call this function for each target Pauli separately. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)] + + +### tighten\_with\_speed\_limit + + + Tighten the provided bounds using limits on the speed of information propagation. + + Inspired by the ideas behind the Lieb-Robinson bounds, this function leverages limits on the speed of information propagation to tighten previously computed forward-evolved unequal-time commutator bounds (see also [`compute_forward_bounds()`](#qiskit_addon_slc.bounds.compute_forward_bounds "qiskit_addon_slc.bounds.compute_forward_bounds")). + + **Parameters** + + * **bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*]*) – the previously computed forward-evolved unequal-time commutator bounds. + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the target circuit. + * **noise\_model\_paulis** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)*]*) – the Pauli error terms to consider for each noise model. + * **observable** ([*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli) *|*[*PauliList*](/docs/api/qiskit/qiskit.quantum_info.PauliList) *|*[*SparseObservable*](/docs/api/qiskit/qiskit.quantum_info.SparseObservable) *|*[*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – the target observable to be measured at the end of the circuit. + + **Returns** + + A tightened copy of the unequal-time commutator bounds. + + **Raises** + + * [**NotImplementedError**](https://docs.python.org/3/library/exceptions.html#NotImplementedError) – when the `observable` contains more than a single Pauli term. If you run into this, you will need to call this function for each target Pauli separately. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – when encountering a gate that acts on more than 2 qubits. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)] + + +### compute\_backward\_bounds + + + Compute the backward-evolved unequal-time commutator bounds. + + Starting at the beginning of the circuit, compute the backward-evolved unequal-time commutator bounds for all Pauli error terms of each noisy layer in the target circuit. + + That is, compute $\| \left[ E_I, \rho_I \right] \|_1$ (using the Schatten 1-norm aka nuclear norm) for all error terms, $E_I$, where $\rho_I$ is assumed to be the all-zero state, $\ket{0 \ldots 0}$, on all active qubits in `circuit`. + + The error terms, $E_I$, are dictated by `noise_model_paulis`. This dictionary maps noise model identifiers (`samplomatic.InjectNoise.ref`) to a list of Pauli error terms. The corresponding terms will be used whenever a [`BoxOp`](/docs/api/qiskit/qiskit.circuit.BoxOp) with a matching [`InjectNoise`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.InjectNoise.html#samplomatic.InjectNoise "(in samplomatic)") annotation is encountered during the iteration over `circuit`. + + + Before computing the bounds, this function removes **all** `Measure` operations from `circuit`. This is required because the circuit is being inverted before being processed in reverse order, which allows the backward evolution to be treated like a forward evolution (in the inverted circuit). + + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the target circuit. + * **noise\_model\_paulis** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)*]*) – the Pauli error terms to consider for each noise model. + * **evolution\_max\_terms** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the maximum number of operator terms to keep track of during the evolution. (If the operator exceeds this size, the smallest terms are truncated). + * **num\_processes** ([*int*](https://docs.python.org/3/library/functions.html#int)) – the number of parallel processes to use. + * **timeout** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) – an optional timeout (in seconds) after which all remaining layers are filled with trivial numerical bounds of `2.0`. Note, that this is not a strict timeout and the layer being processed at the time of reaching this timeout will complete normally. + + **Returns** + + The backward-evolved unequal-time commutator bounds. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)] + + +### merge\_bounds + + + Merge forward and backward bounds. + + The layer at which the switch from using backward bounds to using forward bounds takes place will be the same for all qubits. It is determined by taking into account the provided learned `noise_rates`. If these are not provided, uniform noise rates are assumed. While this is an unrealistic assumption, previewing the merged bounds may still be useful. + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the target circuit. + * **forward\_bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*] | None*) – the forward bounds (see also [`compute_forward_bounds()`](#qiskit_addon_slc.bounds.compute_forward_bounds "qiskit_addon_slc.bounds.compute_forward_bounds")). + * **backward\_bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*] | None*) – the backward bounds (see also [`compute_backward_bounds()`](#qiskit_addon_slc.bounds.compute_backward_bounds "qiskit_addon_slc.bounds.compute_backward_bounds")). + * **noise\_rates** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap) *| None] | None*) – the noise rates learned on the target backend. + * **is\_clifford\_circuit** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – whether the target circuit is fully Clifford. + + **Returns** + + The merged bounds. + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – when both provided bounds are `None`. + * [**KeyError**](https://docs.python.org/3/library/exceptions.html#KeyError) – when the `bounds` contain an `InjectNoise.modifier_ref` key which does not occur in the target `circuit` or whose `InjectNoise.ref` is not found. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if the noise model Pauli terms whose bounds are computed for a given `InjectNoise.modifier_ref` do not match between the `forward_bounds` and `backward_bounds`. + * [**NotImplementedError**](https://docs.python.org/3/library/exceptions.html#NotImplementedError) – when `is_clifford_circuit` is `True`. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)] | None + + +### compute\_local\_scales + + + Computes the `local_scales` argument to a [`Samplex`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.samplex.Samplex.html#samplomatic.samplex.Samplex "(in samplomatic)"). + + This `local_scales` argument is used to specify which individual error terms to mitigate. + + Either the `sampling_cost_budget` or `bias_tolerance` must be specified. The former puts an upper bound on the sampling cost while the latter puts an upper bound on the remaining bias to tolerate. + + + If the order of Pauli terms in `bounds` and `noise_rates` do not match, the output of this function will assume the order set forth by `noise_rates` in order to ensure that the scales are compatible with the rates that will also be provided to the `QuantumProgram`. + + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the target circuit. + * **bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*]*) – the shaded lightcone bounds. + * **noise\_rates** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap) *| None]*) – the learned noise model rates. + * **sampling\_cost\_budget** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the maximum sampling cost to allow. + * **bias\_tolerance** ([*float*](https://docs.python.org/3/library/functions.html#float)) – the maximum bias to tolerate. + + **Returns** + + * the `local_scales` dictionary to be provided as the direct input to the [`samplomatic.samplex.Samplex.inputs()`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.samplex.Samplex.html#samplomatic.samplex.Samplex.inputs "(in samplomatic)"). + * the sampling cost overhead ($\gamma^2$) required to perform the sampling of `local_scales`. + * the remaining bias on expectation values computed with these bounds. + + **Return type** + + A tuple of length 3, the items of which are + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – if non-default values are provided for both, the `sampling_cost_budget` and `bias_tolerance`. + * [**KeyError**](https://docs.python.org/3/library/exceptions.html#KeyError) – if `noise_rates` is missing an entry for any noise model identifier (`InjectNoise.ref`) encountered in `circuit`. + + diff --git a/docs/api/qiskit-addon-slc/index.mdx b/docs/api/qiskit-addon-slc/index.mdx new file mode 100644 index 00000000000..c5e48e0372e --- /dev/null +++ b/docs/api/qiskit-addon-slc/index.mdx @@ -0,0 +1,13 @@ +--- +title: Shaded lightcones API documentation (latest version) +description: Index of all the modules in the latest version of qiskit-addon-slc. +--- + + + +# `qiskit-addon-slc` API reference + +* [Bounds (`qiskit_addon_slc.bounds`)](bounds) +* [Utilities (`qiskit_addon_slc.utils`)](utils) +* [Visualization Tools (`qiskit_addon_slc.visualization`)](visualization) + diff --git a/docs/api/qiskit-addon-slc/release-notes.mdx b/docs/api/qiskit-addon-slc/release-notes.mdx new file mode 100644 index 00000000000..18f96f5e807 --- /dev/null +++ b/docs/api/qiskit-addon-slc/release-notes.mdx @@ -0,0 +1,12 @@ +--- +title: Shaded lightcones release notes +description: Changes made to Shaded lightcones +in_page_toc_max_heading_level: 2 +--- + + + + + +# Shaded lightcones release notes + diff --git a/docs/api/qiskit-addon-slc/utils.mdx b/docs/api/qiskit-addon-slc/utils.mdx new file mode 100644 index 00000000000..460f538e618 --- /dev/null +++ b/docs/api/qiskit-addon-slc/utils.mdx @@ -0,0 +1,171 @@ +--- +title: utils (latest version) +description: API reference for qiskit_addon_slc.utils in the latest version of qiskit-addon-slc +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_slc.utils +--- + + + + + +# Utilities + +`qiskit_addon_slc.utils` + +Various utilities. + +This module provides a number of utility functions. Some of these exist only temporarily to work around open issues of the Qiskit SDK. When this is the case, they are marked as such and may be removed without deprecation or further notice. + +### find\_indices + + + Find the qubit-indices of the provided bit indices or circuit instructions. + + + This function is not considered part of the stable API! It will get removed without warning or deprecation when the same functionality is supported natively by the Qiskit SDK. See [this issue](https://github.com/Qiskit/qiskit/issues/14558) for more details. + + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the quantum circuit whose qubit indices to find. + * **bits\_or\_instruction** ([*Bit*](/docs/api/qiskit/circuit#qiskit.circuit.Bit) *|*[*CircuitInstruction*](/docs/api/qiskit/qiskit.circuit.CircuitInstruction) *|*[*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*Bit*](/docs/api/qiskit/circuit#qiskit.circuit.Bit)*]*) – the bits whose indices to find. If this is a [`CircuitInstruction`](/docs/api/qiskit/qiskit.circuit.CircuitInstruction), the qubits which this instruction acts upon are used. + + **Returns** + + The indices of the queried bits in the circuit’s registers. If a single bit object was provided, a single `int` is returned for its index. Otherwise the return type will be a `list[int]` whose length equals the number of provided bits. + + **Raises** + + [**TypeError**](https://docs.python.org/3/library/exceptions.html#TypeError) – when an unexpected type of `bits_or_instruction` gets provided. + + **Return type** + + [int](https://docs.python.org/3/library/functions.html#int) | [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)] + + +### generate\_noise\_model\_paulis + + + Generate the 1- and 2-weight Pauli terms for each of the unique 2q layer boxes provided. + + **Parameters** + + * **instructions** ([*list*](https://docs.python.org/3/library/stdtypes.html#list)*\[*[*CircuitInstruction*](/docs/api/qiskit/qiskit.circuit.CircuitInstruction)*]*) – the output of [`find_unique_box_instructions()`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.utils.find_unique_box_instructions.html#samplomatic.utils.find_unique_box_instructions "(in samplomatic)"). Any of the provided instructions are assumed to either consists of only measurement gates or correspond to a layer of 2-qubit gate instructions. For the former, the generated noise model will contain only single-qubit `X` errors, for the latter all 1- and 2-weight Pauli errors on the reduced coupling map will be included. + * **coupling\_map** ([*CouplingMap*](/docs/api/qiskit/qiskit.transpiler.CouplingMap) *| None*) – the coupling map of the backend on which the instructions have been laid out. If this is `None`, a 1d line of qubits is assumed. + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) *| None*) – the transpiled circuit which has been laid out on the provided coupling map. This may only be `None` when the `coupling_map` is also `None`. + + **Returns** + + A dictionary mapping the `ref` attributes of the [`InjectNoise`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.InjectNoise.html#samplomatic.InjectNoise "(in samplomatic)") annotation of each unique Box to the 1- and 2-weight Pauli terms whose errors are learned for this box. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)] + + +### get\_extremal\_eigenvalue + + + Finds the extremal eigenvalue of the provided operator. + + This converts the provided operator to a sparse matrix whose minimal eigenvalue is required. + + + The current implementation is definitely not optimized in terms of performance. + + + **Parameters** + + * **spo** ([*SparsePauliOp*](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp)) – the operator whose minimal eigenvalue to find. + + * **kwargs** – + + additional keyword arguments for [`davidson1()`](https://pyscf.org/pyscf_api_docs/pyscf.lib.html#pyscf.lib.linalg_helper.davidson1). When not specified otherwise, the following defaults will be used: + + * tol: 1e-6 + * max\_cycle: 500 + * max\_space: 12 + * lindep: 1e-11 + * max\_memory: 2000 + + Other values will default to PySCF’s default values. + + **Returns** + + A pair indicating whether the Davidson algorithm has converged and the obtained minimal eigenvalue. + + **Return type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[bool](https://docs.python.org/3/library/functions.html#bool), [float](https://docs.python.org/3/library/functions.html#float)] + + +### iter\_circuit + + + Iterates over the instructions in a circuit. + + + This function recurses into [`BoxOp`](/docs/api/qiskit/qiskit.circuit.BoxOp) instructions. + + + + Barriers in the circuit are being ignored. + + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit to iterate over. + * **reverse** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – whether to iterate the circuit in reverse order. + * **log\_process** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – whether to log process. + + **Yields** + + Tuples of length four, consisting of the encountered circuit instruction, the [canonical qubit indices](https://qiskit.github.io/samplomatic/guides/samplex_io.html#qubit-ordering-convention) (i.e. the integer indices of the acted-upon qubits in the context of the input `circuit`), the [`InjectNoise`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.InjectNoise.html#samplomatic.InjectNoise "(in samplomatic)") attributes: `modifier_ref` and `ref`. The latter two items may be `None` indicating a circuit instruction that was **not** part of an unrolled [`BoxOp`](/docs/api/qiskit/qiskit.circuit.BoxOp). + + **Return type** + + [*Generator*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Generator)\[[tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[*CircuitInstruction*](/docs/api/qiskit/qiskit.circuit.CircuitInstruction), [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)], [str](https://docs.python.org/3/library/stdtypes.html#str) | None, [str](https://docs.python.org/3/library/stdtypes.html#str) | None]] + + +### map\_modifier\_ref\_to\_ref + + + Iterate a circuit and map [`InjectNoise`](https://qiskit.github.io/samplomatic/api/auto/samplomatic.InjectNoise.html#samplomatic.InjectNoise "(in samplomatic)") annotation references. + + **Parameters** + + **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit to iterate over. + + **Returns** + + A dictionary mapping each `InjectNoise.modifier_ref` to its `InjectNoise.ref`. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [str](https://docs.python.org/3/library/stdtypes.html#str)] + + +### remove\_measure + + + Remove any [`Measure`](/docs/api/qiskit/circuit#qiskit.circuit.Measure) operations from the provided circuit. + + + This function recurses into [`BoxOp`](/docs/api/qiskit/qiskit.circuit.BoxOp) instructions. + + + **Parameters** + + **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit whose measurements to remove. + + **Returns** + + The circuit without any Measure operations. + + **Return type** + + [*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) + + diff --git a/docs/api/qiskit-addon-slc/visualization.mdx b/docs/api/qiskit-addon-slc/visualization.mdx new file mode 100644 index 00000000000..6c71d693d81 --- /dev/null +++ b/docs/api/qiskit-addon-slc/visualization.mdx @@ -0,0 +1,108 @@ +--- +title: visualization (latest version) +description: API reference for qiskit_addon_slc.visualization in the latest version of qiskit-addon-slc +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_addon_slc.visualization +--- + + + + + +# Visualization Tools + +`qiskit_addon_slc.visualization` + +Visualization methods for shaded lightcones. + +This module provides visualization methods for shaded lightcones. + +### draw\_shaded\_lightcone + + + Draws a shaded lightcone. + + First, the provided `bounds` are accumulated and filtered according to `pauli_filter`. See also [`accumulate_filtered_bounds()`](#qiskit_addon_slc.visualization.accumulate_filtered_bounds "qiskit_addon_slc.visualization.accumulate_filtered_bounds"). Then, the resulting bounds are overlaid onto the provided `circuit` (see [`overlay_bounds_onto_circuit()`](#qiskit_addon_slc.visualization.overlay_bounds_onto_circuit "qiskit_addon_slc.visualization.overlay_bounds_onto_circuit")) and subsequently rendered (see [`render_bounds()`](#qiskit_addon_slc.visualization.render_bounds "qiskit_addon_slc.visualization.render_bounds")). + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit whose shaded lightcone to draw. + * **bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*]*) – the bounds to use for the shaded lightcone. + * **noise\_model\_paulis** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)*]*) – the Pauli error terms of the circuit’s noise models. + * **pauli\_filter** ([*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – the optional Pauli type by which the bounds were filtered. + * **include\_empty\_boxes** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – whether to include empty boxes or not. + * **rendering\_kwargs** – any additional keyword arguments are forwarded to `QuantumCircuit.draw()`. + + **Returns** + + The `mpl` figure. + + **Return type** + + *Figure* + + +### accumulate\_filtered\_bounds + + + Accumulates the bound values filtered by a specified Pauli type. + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit whose bounds to accumulate. + * **bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*]*) – the bounds whose values to accumulate. + * **noise\_model\_paulis** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*QubitSparsePauliList*](/docs/api/qiskit/qiskit.quantum_info.QubitSparsePauliList)*]*) – the Pauli noise terms for each noise model. + * **pauli\_filter** ([*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – the optional Pauli type to filter by. It behaves as follows: - `None`: accumulates all noise term bounds of equal support. - `int`: only accumulates noise term bounds of the specified Pauli weight. - `str`: selects this specific Pauli noise term. - `Pauli`: selects this specific Pauli noise term. + + **Returns** + + A nested dictionary. The outer most key is the `InjectNoise.modifier_ref` (same as the original `bounds`). The next key is the support of the accumulated and filtered bound value (i.e. a tuple of integers). This maps to the actual bound value as a float. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[str](https://docs.python.org/3/library/stdtypes.html#str), [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), …], [float](https://docs.python.org/3/library/functions.html#float)]] + + +### overlay\_bounds\_onto\_circuit + + + Overlays the bounds onto the circuit. + + This produces a new “QuantumCircuit” with “fake” gates reproducing those `BoxOp` instructions from the original `circuit` that had an `InjectNoise` annotation. Those boxes are replaced by visual representations of the computed bounds for Pauli errors of their respective support (see also [`accumulate_filtered_bounds()`](#qiskit_addon_slc.visualization.accumulate_filtered_bounds "qiskit_addon_slc.visualization.accumulate_filtered_bounds")). + + **Parameters** + + * **pauli\_bounds** ([*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*,* [*dict*](https://docs.python.org/3/library/stdtypes.html#dict)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...],* [*float*](https://docs.python.org/3/library/functions.html#float)*]]*) – the accumulated and filtered bounds. + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the circuit on which to overlay the bounds. + * **include\_empty\_boxes** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – whether to include empty boxes or not. + + **Returns** + + A “fake” quantum circuit with “Gate” instructions displaying the computed bound values. The returned quantum circuit’s `QuantumCircuit.metadata` contains the maximum bound value under “max\_bound”. + + **Return type** + + [*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) + + +### render\_bounds + + + Renders a quantum circuit with overlaid bounds and according styling. + + **Parameters** + + * **bounds\_circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – the quantum circuit with overlaid bounds. See also [`overlay_bounds_onto_circuit()`](#qiskit_addon_slc.visualization.overlay_bounds_onto_circuit "qiskit_addon_slc.visualization.overlay_bounds_onto_circuit"). + * **pauli\_filter** ([*Pauli*](/docs/api/qiskit/qiskit.quantum_info.Pauli) *|*[*str*](https://docs.python.org/3/library/stdtypes.html#str) *|*[*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – the optional Pauli type by which the bounds were filtered. This will be indicated in the produced figure’s title. + * **kwargs** – any additional keyword arguments are forwarded to `QuantumCircuit.draw()`. + + **Returns** + + The `mpl` figure. + + **Return type** + + *Figure* + + diff --git a/docs/api/qiskit-addon-sqd/_toc.json b/docs/api/qiskit-addon-sqd/_toc.json index ed5c0c5b297..0b5a0466148 100644 --- a/docs/api/qiskit-addon-sqd/_toc.json +++ b/docs/api/qiskit-addon-sqd/_toc.json @@ -37,5 +37,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-sqd", + "parentLabel": "Sample-based quantum diagonalization (SQD)" } diff --git a/docs/api/qiskit-addon-sqd/release-notes.mdx b/docs/api/qiskit-addon-sqd/release-notes.mdx index cf531e2f271..a1cb27a832f 100644 --- a/docs/api/qiskit-addon-sqd/release-notes.mdx +++ b/docs/api/qiskit-addon-sqd/release-notes.mdx @@ -32,9 +32,9 @@ in_page_toc_max_heading_level: 2 ### New Features -* Added the `max_dim` argument to the [`qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian()`](fermion#diagonalize_fermionic_hamiltonian "qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian") function. This argument is used to limit the dimension of the SCI subspace. +* Added the `max_dim` argument to the [`qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian()`](/docs/api/qiskit-addon-sqd/fermion#diagonalize_fermionic_hamiltonian "qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian") function. This argument is used to limit the dimension of the SCI subspace. -* Introduced the [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left") function. +* Introduced the [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left") function. @@ -42,25 +42,25 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* [`qiskit_addon_sqd.fermion.SCIState`](fermion#scistate "qiskit_addon_sqd.fermion.SCIState") now takes two additional required arguments, `norb` and `nelec`. This is a breaking change. +* [`qiskit_addon_sqd.fermion.SCIState`](/docs/api/qiskit-addon-sqd/fermion#scistate "qiskit_addon_sqd.fermion.SCIState") now takes two additional required arguments, `norb` and `nelec`. This is a breaking change. ### Deprecation Notes -* [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") is now deprecated. Instead, use [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left"). +* [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](/docs/api/qiskit-addon-sqd/configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") is now deprecated. Instead, use [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left"). -* [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample") is now deprecated. Instead, use [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left") and [`qiskit_addon_sqd.subsampling.subsample()`](subsampling#subsample "qiskit_addon_sqd.subsampling.subsample"). +* [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample") is now deprecated. Instead, use [`qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_by_hamming_right_and_left "qiskit_addon_sqd.subsampling.postselect_by_hamming_right_and_left") and [`qiskit_addon_sqd.subsampling.subsample()`](/docs/api/qiskit-addon-sqd/subsampling#subsample "qiskit_addon_sqd.subsampling.subsample"). ### Bug Fixes -* Fixed a bug in [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs") in which the conversion of bitstrings to CI strings could be incorrect when the number of orbitals is greater than 53. +* Fixed a bug in [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](/docs/api/qiskit-addon-sqd/fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs") in which the conversion of bitstrings to CI strings could be incorrect when the number of orbitals is greater than 53. -* Fixed a bug in which saving and loading [`qiskit_addon_sqd.fermion.SCIState`](fermion#scistate "qiskit_addon_sqd.fermion.SCIState") objects resulted in an error. +* Fixed a bug in which saving and loading [`qiskit_addon_sqd.fermion.SCIState`](/docs/api/qiskit-addon-sqd/fermion#scistate "qiskit_addon_sqd.fermion.SCIState") objects resulted in an error. -* Added a release note to version 0.11 for a breaking change involving [`qiskit_addon_sqd.fermion.SCIState`](fermion#scistate "qiskit_addon_sqd.fermion.SCIState"). +* Added a release note to version 0.11 for a breaking change involving [`qiskit_addon_sqd.fermion.SCIState`](/docs/api/qiskit-addon-sqd/fermion#scistate "qiskit_addon_sqd.fermion.SCIState"). @@ -74,9 +74,9 @@ in_page_toc_max_heading_level: 2 ### New Features -* Added a new function [`qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian()`](fermion#diagonalize_fermionic_hamiltonian "qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian") to serve as the main entrypoint to the SQD algorithm. See the updated [tutorials](https://qiskit.github.io/qiskit-addon-sqd/tutorials/index.html) for a demonstration of how to use this function. +* Added a new function [`qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian()`](/docs/api/qiskit-addon-sqd/fermion#diagonalize_fermionic_hamiltonian "qiskit_addon_sqd.fermion.diagonalize_fermionic_hamiltonian") to serve as the main entrypoint to the SQD algorithm. See the updated [tutorials](https://qiskit.github.io/qiskit-addon-sqd/tutorials/index.html) for a demonstration of how to use this function. -* [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") now accepts a `shift` argument used to shift states which have the wrong spin, $(H + shift * S^2)|ψ> = E|ψ>$. +* [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") now accepts a `shift` argument used to shift states which have the wrong spin, $(H + shift * S^2)|ψ> = E|ψ>$. @@ -84,7 +84,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") now accept trailing `kwargs`, which will be passed directly to [pyscf.fci.selected\_ci.kernel\_fixed\_space](https://pyscf.org/pyscf_api_docs/pyscf.fci.html#pyscf.fci.selected_ci.kernel_fixed_space) under the hood to calculate the target state. The `max_davidson` argument should now be passed as `max_cycle` in both functions. +* [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") now accept trailing `kwargs`, which will be passed directly to [pyscf.fci.selected\_ci.kernel\_fixed\_space](https://pyscf.org/pyscf_api_docs/pyscf.fci.html#pyscf.fci.selected_ci.kernel_fixed_space) under the hood to calculate the target state. The `max_davidson` argument should now be passed as `max_cycle` in both functions. @@ -106,7 +106,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* Removed `qiskit_addon_sqd.fermion.flip_orbital_occupancies()`. Users no longer need to flip the orbital occupancies output from [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals"); they will be output in the order expected by [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations"): `tuple(array([occ_a_0, ..., occ_a_N]), array([occ_b_0, ..., occ_b_N]))`. +* Removed `qiskit_addon_sqd.fermion.flip_orbital_occupancies()`. Users no longer need to flip the orbital occupancies output from [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals"); they will be output in the order expected by [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](/docs/api/qiskit-addon-sqd/configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations"): `tuple(array([occ_a_0, ..., occ_a_N]), array([occ_b_0, ..., occ_b_N]))`. @@ -114,7 +114,7 @@ in_page_toc_max_heading_level: 2 ### Deprecation Notes -* The `avg_occupancies` argument to [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations") should now be a length-2 tuple containing the spin-up and spin-down occupancies, respectively. +* The `avg_occupancies` argument to [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](/docs/api/qiskit-addon-sqd/configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations") should now be a length-2 tuple containing the spin-up and spin-down occupancies, respectively. Old format: `array([occ_b_N, ..., occ_b_0, occ_a_N, ..., occ_a_0])` @@ -126,7 +126,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixed a bug which would cause the energy output from [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") to be incorrect when the input `spin_sq` deviates from the spin^2 of the output wavefunction. +* Fixed a bug which would cause the energy output from [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") to be incorrect when the input `spin_sq` deviates from the spin^2 of the output wavefunction. @@ -140,7 +140,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") and [`qiskit_addon_sqd.fermion.rotate_integrals()`](fermion#rotate_integrals "qiskit_addon_sqd.fermion.rotate_integrals") now require `k_flat` to specify the upper triangle (not including diagonal) of the rotation matrix, rather than the entire matrix. +* [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") and [`qiskit_addon_sqd.fermion.rotate_integrals()`](/docs/api/qiskit-addon-sqd/fermion#rotate_integrals "qiskit_addon_sqd.fermion.rotate_integrals") now require `k_flat` to specify the upper triangle (not including diagonal) of the rotation matrix, rather than the entire matrix. @@ -148,7 +148,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixed an indexing error in [`qiskit_addon_sqd.configuration_recovery()`](configuration-recovery#module-qiskit_addon_sqd.configuration_recovery "qiskit_addon_sqd.configuration_recovery") which caused bits in the right half of the bitstring to be flipped with respect to the occupancies of the oritals associated with the left half of the bitstring. +* Fixed an indexing error in [`qiskit_addon_sqd.configuration_recovery()`](/docs/api/qiskit-addon-sqd/configuration-recovery#module-qiskit_addon_sqd.configuration_recovery "qiskit_addon_sqd.configuration_recovery") which caused bits in the right half of the bitstring to be flipped with respect to the occupancies of the oritals associated with the left half of the bitstring. * pyscf is no longer considered a dependency on Windows, where it fails to install. However, Windows remains an unsupported platform. @@ -174,7 +174,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* The ground state returned by [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") will now be an instance of [`qiskit_addon_sqd.fermion.SCIState`](fermion#scistate "qiskit_addon_sqd.fermion.SCIState"), rather than a PySCF `SCIVector` instance. +* The ground state returned by [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") will now be an instance of [`qiskit_addon_sqd.fermion.SCIState`](/docs/api/qiskit-addon-sqd/fermion#scistate "qiskit_addon_sqd.fermion.SCIState"), rather than a PySCF `SCIVector` instance. @@ -188,9 +188,9 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixed a bug in [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") which causes the determinants for spin-up and spin-down to be incorrectly flipped before solving. +* Fixed a bug in [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") which causes the determinants for spin-up and spin-down to be incorrectly flipped before solving. -* Fixed a bug in open-shell workflows which would cause [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") to crash with a `malloc` error. +* Fixed a bug in open-shell workflows which would cause [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") to crash with a `malloc` error. @@ -204,7 +204,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* Specifying `addresses` as a keyword argument to [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") is no longer supported. Users may still pass `addresses` as the first positional argument; however, this usage is deprecated. Users are encouraged to pass the bitstring matrix defining the subspace as the first positional arguments to these functions, as shown below. +* Specifying `addresses` as a keyword argument to [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") is no longer supported. Users may still pass `addresses` as the first positional argument; however, this usage is deprecated. Users are encouraged to pass the bitstring matrix defining the subspace as the first positional arguments to these functions, as shown below. To upgrade, change this code @@ -255,7 +255,7 @@ in_page_toc_max_heading_level: 2 ### Deprecation Notes -* The `qiskit_addon_sqd.fermion.bitstring_matrix_to_sorted_addresses()` function has been deprecated in favor of [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs"). These two functions behave the same with one key exception – `qiskit_addon_sqd.fermion.bitstring_matrix_to_sorted_addresses()` returns the configurations as `tuple(spin_dn, spin_up)`; whereas, [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs") returns the configurations as `tuple(spin_up, spin_dn)`. +* The `qiskit_addon_sqd.fermion.bitstring_matrix_to_sorted_addresses()` function has been deprecated in favor of [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](/docs/api/qiskit-addon-sqd/fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs"). These two functions behave the same with one key exception – `qiskit_addon_sqd.fermion.bitstring_matrix_to_sorted_addresses()` returns the configurations as `tuple(spin_dn, spin_up)`; whereas, [`qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs()`](/docs/api/qiskit-addon-sqd/fermion#bitstring_matrix_to_ci_strs "qiskit_addon_sqd.fermion.bitstring_matrix_to_ci_strs") returns the configurations as `tuple(spin_up, spin_dn)`. To migrate @@ -276,7 +276,7 @@ in_page_toc_max_heading_level: 2 ci_strs_up, ci_strs_dn = bitstring_matrix_to_ci_strs(bs_matrix, open_shell=True) ``` -* The `addresses` argument to [`qiskit_addon_sqd.fermion.solve_fermion()`](fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") has been deprecated in favor of `bitstring_matrix`. Users are no longer required to convert their configurations to integers; instead, they should now pass in the bitstring matrix specifying the subspace onto which to project and diagonalize the Hamiltonian. The conversion to the integer representation of determinants will be done internally. +* The `addresses` argument to [`qiskit_addon_sqd.fermion.solve_fermion()`](/docs/api/qiskit-addon-sqd/fermion#solve_fermion "qiskit_addon_sqd.fermion.solve_fermion") and [`qiskit_addon_sqd.fermion.optimize_orbitals()`](/docs/api/qiskit-addon-sqd/fermion#optimize_orbitals "qiskit_addon_sqd.fermion.optimize_orbitals") has been deprecated in favor of `bitstring_matrix`. Users are no longer required to convert their configurations to integers; instead, they should now pass in the bitstring matrix specifying the subspace onto which to project and diagonalize the Hamiltonian. The conversion to the integer representation of determinants will be done internally. @@ -284,7 +284,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixed a bug in [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations") which would sometimes cause a divide-by-zero error when calculating individual bit-flip probability. +* Fixed a bug in [`qiskit_addon_sqd.configuration_recovery.recover_configurations()`](/docs/api/qiskit-addon-sqd/configuration-recovery#recover_configurations "qiskit_addon_sqd.configuration_recovery.recover_configurations") which would sometimes cause a divide-by-zero error when calculating individual bit-flip probability. * Fixes a bug that caused configuration recovery to fail on bitstrings of length greater than 72. @@ -300,7 +300,7 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* The [`qiskit_addon_sqd.counts.generate_counts_bipartite_hamming()`](counts#generate_counts_bipartite_hamming "qiskit_addon_sqd.counts.generate_counts_bipartite_hamming"), [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample"), and [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") now require the `hamming_right` and `hamming_left` arguments to be specified as keyword arguments. Additionally, the `samples_per_batch` and `n_batches` arguments to [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample") should now be passed as keyword arguments. +* The [`qiskit_addon_sqd.counts.generate_counts_bipartite_hamming()`](/docs/api/qiskit-addon-sqd/counts#generate_counts_bipartite_hamming "qiskit_addon_sqd.counts.generate_counts_bipartite_hamming"), [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample"), and [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](/docs/api/qiskit-addon-sqd/configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") now require the `hamming_right` and `hamming_left` arguments to be specified as keyword arguments. Additionally, the `samples_per_batch` and `n_batches` arguments to [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample") should now be passed as keyword arguments. To upgrade @@ -370,7 +370,7 @@ This is a minor release which introduces a couple of small, but important, break ### Upgrade Notes -* The [`qiskit_addon_sqd.counts.generate_counts_bipartite_hamming()`](counts#generate_counts_bipartite_hamming "qiskit_addon_sqd.counts.generate_counts_bipartite_hamming"), [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample"), and [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") now take the `hamming_right` positional argument before the `hamming_left` argument to better match the rest of the workflow. +* The [`qiskit_addon_sqd.counts.generate_counts_bipartite_hamming()`](/docs/api/qiskit-addon-sqd/counts#generate_counts_bipartite_hamming "qiskit_addon_sqd.counts.generate_counts_bipartite_hamming"), [`qiskit_addon_sqd.subsampling.postselect_and_subsample()`](/docs/api/qiskit-addon-sqd/subsampling#postselect_and_subsample "qiskit_addon_sqd.subsampling.postselect_and_subsample"), and [`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight()`](/docs/api/qiskit-addon-sqd/configuration-recovery#post_select_by_hamming_weight "qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight") now take the `hamming_right` positional argument before the `hamming_left` argument to better match the rest of the workflow. To upgrade diff --git a/docs/api/qiskit-addon-utils/_toc.json b/docs/api/qiskit-addon-utils/_toc.json index 890eceb867e..88cfc1dcc4e 100644 --- a/docs/api/qiskit-addon-utils/_toc.json +++ b/docs/api/qiskit-addon-utils/_toc.json @@ -87,5 +87,7 @@ } ], "collapsed": true, - "untranslatable": true + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-addon-utils", + "parentLabel": "Qiskit addon utilities" } diff --git a/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes.mdx b/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes.mdx index 0339b895eca..855b494c0bc 100644 --- a/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes.mdx +++ b/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes.mdx @@ -16,9 +16,9 @@ python_api_name: qiskit_addon_utils.noise_management.post_selection.transpiler.p A submodule with transpilation passes for post selection. -| | | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| [`AddSpectatorMeasures`](noise-management-post-selection-transpiler-passes-add-spectator-measures "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddSpectatorMeasures") | Add measurements on spectator qubits. | -| [`AddPostSelectionMeasures`](noise-management-post-selection-transpiler-passes-add-post-selection-measures "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddPostSelectionMeasures") | Add a post selection measurement after every terminal measurement. | -| [`XSlowGate`](noise-management-post-selection-transpiler-passes-x-slow-gate "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.XSlowGate") | The x-slow gate. | +| | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| [`AddSpectatorMeasures`](/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes-add-spectator-measures "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddSpectatorMeasures") | Add measurements on spectator qubits. | +| [`AddPostSelectionMeasures`](/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes-add-post-selection-measures "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.AddPostSelectionMeasures") | Add a post selection measurement after every terminal measurement. | +| [`XSlowGate`](/docs/api/qiskit-addon-utils/noise-management-post-selection-transpiler-passes-x-slow-gate "qiskit_addon_utils.noise_management.post_selection.transpiler.passes.XSlowGate") | The x-slow gate. | diff --git a/docs/api/qiskit-addon-utils/release-notes.mdx b/docs/api/qiskit-addon-utils/release-notes.mdx index acb2e125492..9656c3bd3b1 100644 --- a/docs/api/qiskit-addon-utils/release-notes.mdx +++ b/docs/api/qiskit-addon-utils/release-notes.mdx @@ -20,7 +20,7 @@ in_page_toc_max_heading_level: 2 ### Bug Fixes -* Fixed bugs in [`qiskit_addon_utils.exp_vals.executor_expectation_values()`](exp-vals#executor_expectation_values "qiskit_addon_utils.exp_vals.executor_expectation_values") causing indexing errors when bit\_array dimensions associated with twirling randomizations were not specified in avg\_axis. +* Fixed bugs in [`qiskit_addon_utils.exp_vals.executor_expectation_values()`](/docs/api/qiskit-addon-utils/exp-vals#executor_expectation_values "qiskit_addon_utils.exp_vals.executor_expectation_values") causing indexing errors when bit\_array dimensions associated with twirling randomizations were not specified in avg\_axis. @@ -32,16 +32,16 @@ in_page_toc_max_heading_level: 2 ### New Features -* Added a new sub-package, [`qiskit_addon_utils.exp_vals`](exp-vals#module-qiskit_addon_utils.exp_vals "qiskit_addon_utils.exp_vals"), containing functions for calculating expectation values from the outputs of Qiskit Runtime’s new `Executor` primitive prototype. This includes: +* Added a new sub-package, [`qiskit_addon_utils.exp_vals`](/docs/api/qiskit-addon-utils/exp-vals#module-qiskit_addon_utils.exp_vals "qiskit_addon_utils.exp_vals"), containing functions for calculating expectation values from the outputs of Qiskit Runtime’s new `Executor` primitive prototype. This includes: - > * [`qiskit_addon_utils.exp_vals.executor_expectation_values()`](exp-vals#executor_expectation_values "qiskit_addon_utils.exp_vals.executor_expectation_values") for expectation value calculation - > * [`qiskit_addon_utils.exp_vals.get_measurement_bases()`](exp-vals#get_measurement_bases "qiskit_addon_utils.exp_vals.get_measurement_bases") for identifying a minimal set of measurement bases to cover all terms in the observable. - > * [`qiskit_addon_utils.exp_vals.map_observable_isa_to_canonical()`](exp-vals#map_observable_isa_to_canonical "qiskit_addon_utils.exp_vals.map_observable_isa_to_canonical"), [`qiskit_addon_utils.exp_vals.map_observable_virtual_to_canonical()`](exp-vals#map_observable_virtual_to_canonical "qiskit_addon_utils.exp_vals.map_observable_virtual_to_canonical"), and [`qiskit_addon_utils.exp_vals.map_observable_isa_to_virtual()`](exp-vals#map_observable_isa_to_virtual "qiskit_addon_utils.exp_vals.map_observable_isa_to_virtual") for transforming the qubit order of Pauli observables. + > * [`qiskit_addon_utils.exp_vals.executor_expectation_values()`](/docs/api/qiskit-addon-utils/exp-vals#executor_expectation_values "qiskit_addon_utils.exp_vals.executor_expectation_values") for expectation value calculation + > * [`qiskit_addon_utils.exp_vals.get_measurement_bases()`](/docs/api/qiskit-addon-utils/exp-vals#get_measurement_bases "qiskit_addon_utils.exp_vals.get_measurement_bases") for identifying a minimal set of measurement bases to cover all terms in the observable. + > * [`qiskit_addon_utils.exp_vals.map_observable_isa_to_canonical()`](/docs/api/qiskit-addon-utils/exp-vals#map_observable_isa_to_canonical "qiskit_addon_utils.exp_vals.map_observable_isa_to_canonical"), [`qiskit_addon_utils.exp_vals.map_observable_virtual_to_canonical()`](/docs/api/qiskit-addon-utils/exp-vals#map_observable_virtual_to_canonical "qiskit_addon_utils.exp_vals.map_observable_virtual_to_canonical"), and [`qiskit_addon_utils.exp_vals.map_observable_isa_to_virtual()`](/docs/api/qiskit-addon-utils/exp-vals#map_observable_isa_to_virtual "qiskit_addon_utils.exp_vals.map_observable_isa_to_virtual") for transforming the qubit order of Pauli observables. -* Added two new functions to the [`qiskit_addon_utils.noise_management`](noise-management#module-qiskit_addon_utils.noise_management "qiskit_addon_utils.noise_management") sub-package: +* Added two new functions to the [`qiskit_addon_utils.noise_management`](/docs/api/qiskit-addon-utils/noise-management#module-qiskit_addon_utils.noise_management "qiskit_addon_utils.noise_management") sub-package: - > * [`qiskit_addon_utils.noise_management.trex_factors()`](noise-management#trex_factors "qiskit_addon_utils.noise_management.trex_factors") for calculating TREX readout error mitigation scaling factors - > * [`qiskit_addon_utils.noise_management.gamma_from_noisy_boxes()`](noise-management#gamma_from_noisy_boxes "qiskit_addon_utils.noise_management.gamma_from_noisy_boxes") for calculating the gamma factor for a given circuit and noise model. + > * [`qiskit_addon_utils.noise_management.trex_factors()`](/docs/api/qiskit-addon-utils/noise-management#trex_factors "qiskit_addon_utils.noise_management.trex_factors") for calculating TREX readout error mitigation scaling factors + > * [`qiskit_addon_utils.noise_management.gamma_from_noisy_boxes()`](/docs/api/qiskit-addon-utils/noise-management#gamma_from_noisy_boxes "qiskit_addon_utils.noise_management.gamma_from_noisy_boxes") for calculating the gamma factor for a given circuit and noise model. @@ -91,5 +91,5 @@ in_page_toc_max_heading_level: 2 ### Upgrade Notes -* The keyword argument `include_slice_barriers` of [`combine_slices()`](slicing#combine_slices "qiskit_addon_utils.slicing.combine_slices") has been renamed to `include_barriers`. +* The keyword argument `include_slice_barriers` of [`combine_slices()`](/docs/api/qiskit-addon-utils/slicing#combine_slices "qiskit_addon_utils.slicing.combine_slices") has been renamed to `include_barriers`. diff --git a/docs/api/qiskit-addon-utils/slicing-transpiler-passes.mdx b/docs/api/qiskit-addon-utils/slicing-transpiler-passes.mdx index f4ee1a48a78..041efb1adb7 100644 --- a/docs/api/qiskit-addon-utils/slicing-transpiler-passes.mdx +++ b/docs/api/qiskit-addon-utils/slicing-transpiler-passes.mdx @@ -16,9 +16,9 @@ python_api_name: qiskit_addon_utils.slicing.transpiler.passes A submodule with transpilation passes for slicing. -| | | -| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| [`CollectOpColor`](slicing-transpiler-passes-collect-op-color "qiskit_addon_utils.slicing.transpiler.passes.CollectOpColor") | Collects blocks of operations which act on the provided edges. | -| [`CollectOpSize`](slicing-transpiler-passes-collect-op-size "qiskit_addon_utils.slicing.transpiler.passes.CollectOpSize") | Collects blocks of the specified size and replaces them by a single block instruction. | -| [`CollectOpType`](slicing-transpiler-passes-collect-op-type "qiskit_addon_utils.slicing.transpiler.passes.CollectOpType") | Collects blocks of the specified operation and replaces them by a single block instruction. | +| | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| [`CollectOpColor`](/docs/api/qiskit-addon-utils/slicing-transpiler-passes-collect-op-color "qiskit_addon_utils.slicing.transpiler.passes.CollectOpColor") | Collects blocks of operations which act on the provided edges. | +| [`CollectOpSize`](/docs/api/qiskit-addon-utils/slicing-transpiler-passes-collect-op-size "qiskit_addon_utils.slicing.transpiler.passes.CollectOpSize") | Collects blocks of the specified size and replaces them by a single block instruction. | +| [`CollectOpType`](/docs/api/qiskit-addon-utils/slicing-transpiler-passes-collect-op-type "qiskit_addon_utils.slicing.transpiler.passes.CollectOpType") | Collects blocks of the specified operation and replaces them by a single block instruction. | diff --git a/docs/api/qiskit-addon-utils/slicing.mdx b/docs/api/qiskit-addon-utils/slicing.mdx index 80d109c14e6..e21c5fda9bd 100644 --- a/docs/api/qiskit-addon-utils/slicing.mdx +++ b/docs/api/qiskit-addon-utils/slicing.mdx @@ -66,7 +66,7 @@ For more information, check out the [how-to guide](https://qiskit.github.io/qisk Split a `QuantumCircuit` into slices using the provided edge coloring. - Two-qubit gates acting on identically colored qubit connections (edges) will be grouped greedily into slices using [`CollectOpColor`](slicing-transpiler-passes-collect-op-color "qiskit_addon_utils.slicing.transpiler.passes.CollectOpColor"). This will be done in order of increasing color value (the integer values which each edge is mapped to). + Two-qubit gates acting on identically colored qubit connections (edges) will be grouped greedily into slices using [`CollectOpColor`](/docs/api/qiskit-addon-utils/slicing-transpiler-passes-collect-op-color "qiskit_addon_utils.slicing.transpiler.passes.CollectOpColor"). This will be done in order of increasing color value (the integer values which each edge is mapped to). Note, that this does *not* mean that low valued color slices are guaranteed to be left-most in your circuit. Below is an example to emphasize this. @@ -114,7 +114,7 @@ For more information, check out the [how-to guide](https://qiskit.github.io/qisk └───┘ ░ ``` - Single-qubit gates will be collected into a single slice using [`CollectOpSize`](slicing-transpiler-passes-collect-op-size "qiskit_addon_utils.slicing.transpiler.passes.CollectOpSize"). + Single-qubit gates will be collected into a single slice using [`CollectOpSize`](/docs/api/qiskit-addon-utils/slicing-transpiler-passes-collect-op-size "qiskit_addon_utils.slicing.transpiler.passes.CollectOpSize"). **Parameters** diff --git a/docs/api/qiskit-paulice/_package.json b/docs/api/qiskit-paulice/_package.json new file mode 100644 index 00000000000..c707de5d01d --- /dev/null +++ b/docs/api/qiskit-paulice/_package.json @@ -0,0 +1,4 @@ +{ + "name": "qiskit-paulice", + "version": "0.1.0" +} diff --git a/docs/api/qiskit-paulice/_toc.json b/docs/api/qiskit-paulice/_toc.json new file mode 100644 index 00000000000..e3e8d055557 --- /dev/null +++ b/docs/api/qiskit-paulice/_toc.json @@ -0,0 +1,33 @@ +{ + "title": "Qiskit Paulice", + "children": [ + { + "title": "API index", + "url": "/docs/api/qiskit-paulice" + }, + { + "title": "qiskit_paulice.checked_circuit", + "url": "/docs/api/qiskit-paulice/checked-circuit", + "untranslatable": true + }, + { + "title": "qiskit_paulice.checks", + "url": "/docs/api/qiskit-paulice/checks", + "untranslatable": true + }, + { + "title": "qiskit_paulice.layout", + "url": "/docs/api/qiskit-paulice/layout", + "untranslatable": true + }, + { + "title": "qiskit_paulice.noise_models", + "url": "/docs/api/qiskit-paulice/noise-models", + "untranslatable": true + } + ], + "collapsed": true, + "untranslatable": true, + "parentUrl": "/docs/addons/qiskit-paulice", + "parentLabel": "Qiskit Paulice" +} diff --git a/docs/api/qiskit-paulice/checked-circuit.mdx b/docs/api/qiskit-paulice/checked-circuit.mdx new file mode 100644 index 00000000000..3b7701cd0f7 --- /dev/null +++ b/docs/api/qiskit-paulice/checked-circuit.mdx @@ -0,0 +1,177 @@ +--- +title: checked_circuit (latest version) +description: API reference for qiskit_paulice.checked_circuit in the latest version of qiskit-paulice +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_paulice.checked_circuit +--- + + + + + +# Checked circuit + +`qiskit_paulice.checked_circuit` + +A class for specifying a circuit containing coherent spacetime Pauli checks. + +### CheckedCircuit + + + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) + + A quantum circuit and information about spacetime Pauli checks it contains. + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) + * **target\_qubits** ([*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...]*) + * **check\_qubits** ([*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...]*) + * **check\_support** ([*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*tuple*](https://docs.python.org/3/library/stdtypes.html#tuple)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*, ...], ...]*) + * **cost** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) + * **cost\_metric** ([*str*](https://docs.python.org/3/library/stdtypes.html#str) *| None*) + + #### circuit + + + A quantum circuit containing `0` or more spacetime Pauli checks. + + **Type** + + [qiskit.circuit.quantumcircuit.QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) + + + #### target\_qubits + + + Qubit indices of `circuit` which were used to entangle the check qubits to the payload. `None` if `circuit` contains no checks. + + **Type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), …] + + + #### check\_qubits + + + Qubit indices of the ancilla qubits in `circuit`. The `i``th check uses ``check_qubits[i]` to detect errors on `target_qubits[i]` and other qubits in `check_support[i]`. + + **Type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), …] + + + #### check\_support + + + For each check, the qubit indices whose measurement outcomes XOR together to give that check’s syndrome bit. + + **Type** + + [tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[tuple](https://docs.python.org/3/library/stdtypes.html#tuple)\[[int](https://docs.python.org/3/library/functions.html#int), …], …] + + + #### cost + + + The value of the cost function with respect to the checks in `circuit` + + **Type** + + [float](https://docs.python.org/3/library/functions.html#float) | None + + + #### cost\_metric + + + The metric used to evaluate check quality (`gamma` or `LER`) + + **Type** + + [str](https://docs.python.org/3/library/stdtypes.html#str) | None + + + #### get\_postselection\_method + + + Return a function that maps a single shot’s outcome to a syndrome vector. + + No errors were detected iff every entry of the returned vector is zero. The returned function accepts either bitstrings or bit arrays. + + **Return type** + + [*Callable*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)\[\[[str](https://docs.python.org/3/library/stdtypes.html#str) | [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)], [*ndarray*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)] + + + #### uncovered\_paulis + + + Locations where a single qubit Pauli error is undetectable by some checks. + + Each entry is an `UncoveredPauli(qubit, after_instruction, pauli)` triple, where `qubit` is the qubit of the single-qubit error, `after_instruction` is the `circuit.data` index of the instruction which immediately precedes the error, and `pauli` is the type of error (`"X"`, `"Y"`, or `"Z"`). + + Only locations on input wires and immediately after 2-qubit gates are enumerated; errors after single qubit gates are folded into the next 2-qubit-gate wire. + + + +### UncoveredPauli + + + Bases: [`NamedTuple`](https://docs.python.org/3/library/typing.html#typing.NamedTuple) + + A spacetime location at which a single qubit Pauli error is undetectable by a set of checks. + + **Parameters** + + * **qubit** ([*int*](https://docs.python.org/3/library/functions.html#int)) + * **after\_instruction** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) + * **pauli** ([*Literal*](https://docs.python.org/3/library/typing.html#typing.Literal)*\['X', 'Y', 'Z']*) + + #### qubit + + + Index of the qubit where the undetected error sits + + **Type** + + [int](https://docs.python.org/3/library/functions.html#int) + + + #### after\_instruction + + + Index (into `circuit.data`) of the instruction the error occurs after; `None` means the error sits on the qubit’s input wire. + + **Type** + + [int](https://docs.python.org/3/library/functions.html#int) | None + + + #### pauli + + + The undetected Pauli error (`"X"`, `"Y"`, or `"Z"`) + + **Type** + + Literal\[‘X’, ‘Y’, ‘Z’] + + + Create new instance of UncoveredPauli(qubit, after\_instruction, pauli) + + #### count + + + Return number of occurrences of value. + + + #### index + + + Return first index of value. + + Raises ValueError if the value is not present. + + + diff --git a/docs/api/qiskit-paulice/checks.mdx b/docs/api/qiskit-paulice/checks.mdx new file mode 100644 index 00000000000..f375a8e75dc --- /dev/null +++ b/docs/api/qiskit-paulice/checks.mdx @@ -0,0 +1,67 @@ +--- +title: checks (latest version) +description: API reference for qiskit_paulice.checks in the latest version of qiskit-paulice +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_paulice.checks +--- + + + + + +# Checks + +`qiskit_paulice.checks` + +Functionality for finding effective spacetime Pauli checks. + +### add\_pauli\_checks + + + Add spacetime Pauli checks to a Clifford circuit. + + The check picking algorithm finds valid, low weight checks on each target qubit in the order they are specified in `target_qubits` and chooses the check which provides the most error detection coverage (i.e. minimizes the `cost` value). Once a check is committed on `target_qubits[0]`, it will be set for the remainder of the algorithm and a search for a good check on `target_qubits[1]` will begin. For this reason, the ordering of `target_qubits` can have some impact on the set of checks produced by the function. + + This function produces `qiskit_paulice.CheckedCircuit` instances containing numbers of checks ranging from `0` to one check per target qubit. It can be instructive to view the convergence of the cost function as more checks are added, as one may see convergence of the cost using fewer checks. + + For details on finding effective spacetime Pauli checks, see [Supplemental Sec. II-VI of Martiel, Javadi](https://arxiv.org/abs/2504.15725). + + **Parameters** + + * **circuit** ([*QuantumCircuit*](/docs/api/qiskit/qiskit.circuit.QuantumCircuit)) – The Clifford circuit to dress with spacetime Pauli checks. The circuit must be terminated with a measurement on at least one qubit. The circuit may be defined on virtual or physical qubits. If the circuit has a layout, the user must provide `ancilla_qubits`. + + * **target\_qubits** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – Qubit indices of `circuit` which will be used to entangle the check qubits to the payload. When `circuit` has a layout (ISA mode), these are physical qubit indices, in the same index space as `ancilla_qubits`. + + * **noise\_model** ([*NoiseModel*](noise-models#noisemodel "qiskit_paulice.noise_models.NoiseModel")) – A noise model describing the effect of noise on the target device. This model will be used to estimate the effect of a given check during the check picking process. While one can generate a noise model from learned Pauli-Lindblad noise, a rougher approximation of the noise generated from backend benchmark data is often sufficient. Ancilla/target edges introduced by check insertion that aren’t in the supplied `GateWiseNoise` or `LayeredNoise` are auto-inferred (median rate per Pauli pair across the supplied data); supply them explicitly to override. + + * **cost** ([*Literal*](https://docs.python.org/3/library/typing.html#typing.Literal)*\['gamma', 'LER']*) – + + Metric to optimize. Can be `"gamma"` or `"LER"` (logical error rate). + + * `"gamma"`: The gamma value associated with the inverse logical noise channel (i.e. the noise channel consisting of errors within the measurement lightcone that are undetectable by the checks). + * `"LER"`: The empirical logical error rate after postselection. Performs Monte Carlo simulations to calculate the fraction of postselected shots affected by uncovered logical noise. + + * **cost\_nshots** ([*int*](https://docs.python.org/3/library/functions.html#int)) – Number of Monte Carlo shots used by Monte Carlo-based cost metrics (currently only `"LER"`). + + * **method** ([*Literal*](https://docs.python.org/3/library/typing.html#typing.Literal)*\['windowed', 'genetic', 'windowed\_genetic']*) – + + Check picking method (`"windowed"`, `"genetic"`, or `"windowed_genetic"`). Each method will add checks to `target_qubits` sequentially. Once a check is committed to a given target qubit, it will not be undone as more checks are added. Each method picks checks which provide maximum error detection capability (i.e. lowest `cost`). + + * `"windowed"`: Sample small subsets of the wires on each target qubit to find good checks + * `"genetic"`: Evolve a set of candidate checks from the full wire-space of each target qubit + * `"windowed_genetic"`: Run a genetic search within random windows of wires on each target qubit + + * **ancilla\_qubits** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*] | None*) – List of physical qubit indices (one per index in `target_qubits`) specifying where to place the check ancillas in the output circuit. Required when `circuit.layout` is not `None`. `ancilla_qubits[i]` will share entangling gates with `target_qubits[i]` when implementing the `i`\ th check. + + * **check\_creg\_name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – Name of the classical register for check measurements (default: “checks\_c”) + + * **check\_qreg\_name** ([*str*](https://docs.python.org/3/library/stdtypes.html#str)) – Name of the quantum register holding the check ancillas in the output circuits (default: “checks\_q”). Ignored in ISA mode. + + * **seed** ([*int*](https://docs.python.org/3/library/functions.html#int) *| None*) – Random seed for controlling randomness during the search for good checks. While this seed controls some randomness in the algorithm, some non-determinism still exists when using `LER` cost function, or either variety of genetic check picking method. The combination of `cost="gamma"` and `method="windowed"` is fully deterministic if `seed` is not `None`. + + **Returns** + + A list of `qiskit_paulice.CheckedCircuit` instances – instances containing the bare circuit with no checks and one for each added check. The final element in the output contains the `qiskit_paulice.CheckedCircuit` with checks on every target qubit, assuming a valid set of checks could be found. + + diff --git a/docs/api/qiskit-paulice/index.mdx b/docs/api/qiskit-paulice/index.mdx new file mode 100644 index 00000000000..b38af9fb2a7 --- /dev/null +++ b/docs/api/qiskit-paulice/index.mdx @@ -0,0 +1,14 @@ +--- +title: Qiskit Paulice API documentation (latest version) +description: Index of all the modules in the latest version of qiskit-paulice. +--- + + + +# `qiskit-paulice` API reference + +* [Checks (`qiskit_paulice.checks`)](checks) +* [Checked circuit (`qiskit_paulice.checked_circuit`)](checked-circuit) +* [Noise models (`qiskit_paulice.noise_models`)](noise-models) +* [Layout (`qiskit_paulice.layout`)](layout) + diff --git a/docs/api/qiskit-paulice/layout.mdx b/docs/api/qiskit-paulice/layout.mdx new file mode 100644 index 00000000000..0cc38cfb1d4 --- /dev/null +++ b/docs/api/qiskit-paulice/layout.mdx @@ -0,0 +1,39 @@ +--- +title: layout (latest version) +description: API reference for qiskit_paulice.layout in the latest version of qiskit-paulice +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_paulice.layout +--- + + + + + +# Layout + +`qiskit_paulice.layout` + +Functionality for finding layouts for checks dressed with spacetime Pauli checks. + +### get\_low\_overhead\_ancillas + + + Find ancilla qubits adjacent to the layout qubits in the coupling graph. + + This function identifies physical qubits that are not in the layout but are connected to one or more layout qubits via the coupling map. + + **Parameters** + + * **coupling\_map** ([*CouplingMap*](/docs/api/qiskit/qiskit.transpiler.CouplingMap)) – A qubit connectivity graph + * **layout** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – Physical qubit indices for which to find adjacent ancillas + + **Returns** + + A dictionary mapping ancilla qubit indices to lists of layout qubit indices to which it is adjacent. + + **Return type** + + [dict](https://docs.python.org/3/library/stdtypes.html#dict)\[[int](https://docs.python.org/3/library/functions.html#int), [list](https://docs.python.org/3/library/stdtypes.html#list)\[[int](https://docs.python.org/3/library/functions.html#int)]] + + diff --git a/docs/api/qiskit-paulice/noise-models.mdx b/docs/api/qiskit-paulice/noise-models.mdx new file mode 100644 index 00000000000..cc2a0386d58 --- /dev/null +++ b/docs/api/qiskit-paulice/noise-models.mdx @@ -0,0 +1,147 @@ +--- +title: noise_models (latest version) +description: API reference for qiskit_paulice.noise_models in the latest version of qiskit-paulice +in_page_toc_min_heading_level: 2 +python_api_type: module +python_api_name: qiskit_paulice.noise_models +--- + + + + + +# Noise models + +`qiskit_paulice.noise_models` + +Noise model specification for evaluating spacetime Pauli checks. + +### NoiseModel + + + Bases: [`object`](https://docs.python.org/3/library/functions.html#object) + + Noise model used to find optimal circuit locations for spacetime Pauli checks. + + **Parameters** + + * **gate\_noise** (*GateNoise | None*) + * **readout\_noise** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) + * **idling\_noise** ([*float*](https://docs.python.org/3/library/functions.html#float) *| None*) + + #### from\_backend + + + Instantiate a [`NoiseModel`](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") from backend calibration data. + + Edge keys in the resulting `GateWiseNoise` dict use **virtual** qubit indices (positions in `layout`). If the input circuit has a layout that maps virtual to physical consistently with `layout`, the keys also serve as physical indices. + + `idling_noise` is always `None` from this method; if `backend` lacks readout calibration, `readout_noise` will also be `None`. + + **Parameters** + + * **backend** ([*BackendV2*](/docs/api/qiskit/qiskit.providers.BackendV2)) – A backend containing two-qubit gate error probabilities for each coupling map edge as well as readout error information for each qubit in the layout. + * **layout** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*int*](https://docs.python.org/3/library/functions.html#int)*]*) – Physical qubit indices on the backend to include in the noise model. The order defines the virtual qubit indexing (virtual qubit 0 maps to `layout[0]`, etc.). + * **uniform\_gate\_noise** ([*bool*](https://docs.python.org/3/library/functions.html#bool)) – If `True`, the `gate_noise` field in the output [`NoiseModel`](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") will be a `UniformGateNoise` and the error probability is assumed to be distributed uniformly among the `15` non-identity Pauli bases to form a uniform depolarizing channel which will affect all entangling gates equally. If `False`, `gate_noise` will be a `GateWiseNoise` instance where each edge is associated with a custom noise channel based on backend calibration data. + * **pauli\_bases** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*str*](https://docs.python.org/3/library/stdtypes.html#str)*] | None*) – For `GateWiseNoise` models, `pauli_bases` are the bases over which the error probability reported from the backend will be distributed. Each basis is a 2-character Pauli string paired left-to-right with the edge tuple, so `"XZ"` on edge `(a, b)` places `X` on `a` and `Z` on `b`. The default behavior is to use the full 2Q Pauli basis excluding `"II"`. For `UniformNoise`, the full basis is assumed, and this argument is ignored. + + **Returns** + + A [`NoiseModel`](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") instance containing gate and readout noise derived from backend calibration data. + + **Raises** + + [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Backend is missing calibration data or layout contains invalid qubit indices. + + **Return type** + + [*NoiseModel*](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") + + + #### from\_pauli\_lindblad\_maps + + + Create a NoiseModel from Pauli-Lindblad maps. + + This method constructs a [`NoiseModel`](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") from `PauliLindbladMap` instances that represent noise channels affecting gates and, optionally, measurements. Gate noise is specified with the `layer_noise` argument. Each `PauliLindbladMap` instance is assumed to act on a unique layer of entangling gates. Readout noise is specified by a single `PauliLindbladMap` containing single-qubit Pauli-X generators and their associated rates. + + **Parameters** + + * **layer\_noise** ([*Sequence*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence)*\[*[*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)*]*) – A sequence of `PauliLindbladMap` objects, where each map represents the noise channel for one unique entangling layer in the circuit. + * **readout\_noise** ([*PauliLindbladMap*](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap) *| None*) – Optional `PauliLindbladMap` containing Pauli X generators on each qubit for readout errors. + + **Returns** + + A [`NoiseModel`](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") instance reflecting the noise model(s) defined in the input `PauliLindbladMap``s. ``idling_noise` will always be `None` for this method. + + **Raises** + + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Weight of error generator greater than `2`. + * [**ValueError**](https://docs.python.org/3/library/exceptions.html#ValueError) – Readout error generator is not Pauli-X. + + **Return type** + + [*NoiseModel*](#qiskit_paulice.noise_models.NoiseModel "qiskit_paulice.noise_models.NoiseModel") + + + #### gate\_noise + + + Errors that occur during 2-qubit gate operations. Can be: + + * `UniformNoise`: Same error probability for all gates. The error probability is equally distributed to each Pauli basis represented in the channel. + * `LayeredNoise`: Pauli-Lindblad noise channel per unique entangling layer + * `GateWiseNoise`: Pauli-Lindblad noise channel per unique entangling edge + + + #### idling\_noise + + + Qubit decay rate during idle time. Total error probability is given as `1 - exp(-t / idling_noise)`. + + + #### readout\_noise + + + Probability of bit-flip during measurement. + + + +### UniformGateNoise + + + A depolarizing probability applied uniformly to all 2-qubit gates. + + This probability will be equally distributed among the `15` non-identity Paulis in the 2-qubit Pauli basis to form the depolarizing channel. This noise channel will be applied **after** each entangling gate. + + +### LayeredGateNoise + + + Layered noise model mapping unique entangling layers to Pauli error generators. + + + The Pauli-Lindblad error associated with each entangling layer is assumed to be defined **before** the gates in the layer. + + + Keys are tuples of qubit index pairs (edges) defining a layer. Values are lists of `(pauli, rate)` tuples where `pauli` is a Pauli or Pauli string defined over all qubits and `rate` is the associated error rate. + + alias of [`dict`](https://docs.python.org/3/library/stdtypes.html#dict)\[[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`int`](https://docs.python.org/3/library/functions.html#int), [`int`](https://docs.python.org/3/library/functions.html#int)], …], [`list`](https://docs.python.org/3/library/stdtypes.html#list)\[[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`Pauli`](/docs/api/qiskit/qiskit.quantum_info.Pauli) | [`str`](https://docs.python.org/3/library/stdtypes.html#str), [`float`](https://docs.python.org/3/library/functions.html#float)]]] + + +### GateWiseNoise + + + Gate-wise noise model mapping qubit pairs to a Pauli noise channel. + + Keys are tuples of qubit index pairs representing edges. Values are lists of (pauli, rate) tuples where `pauli` is a 2-character Pauli string and `rate` is the associated error rate. The string is paired left-to-right with the edge tuple — matching [`PauliLindbladMap`](/docs/api/qiskit/qiskit.quantum_info.PauliLindbladMap)’s sparse form `(pauli_str, indices)`: for edge `(a, b)`, `"XZ"` places `X` on `a` and `Z` on `b`. + + alias of [`dict`](https://docs.python.org/3/library/stdtypes.html#dict)\[[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`int`](https://docs.python.org/3/library/functions.html#int), [`int`](https://docs.python.org/3/library/functions.html#int)], [`list`](https://docs.python.org/3/library/stdtypes.html#list)\[[`tuple`](https://docs.python.org/3/library/stdtypes.html#tuple)\[[`str`](https://docs.python.org/3/library/stdtypes.html#str), [`float`](https://docs.python.org/3/library/functions.html#float)]]] + + +### GateNoise + + + Gate noise can be uniform (float), layered (dict), or gate-wise (dict). + + diff --git a/docs/guides/addons.mdx b/docs/guides/addons.mdx index 2bd019155a5..b01b4073d55 100644 --- a/docs/guides/addons.mdx +++ b/docs/guides/addons.mdx @@ -7,7 +7,7 @@ description: Understand the Qiskit addon tools, which help you build utility-gra # Advanced techniques - Qiskit addons -Qiskit addons are a collection of research capabilities for enabling algorithm discovery at the utility scale. These modular software components build on Qiskit’s performant foundation and can plug into a [workflow](/docs/guides/intro-to-patterns) to scale or design new quantum algorithms. This page highlights the available tools across key functional categories to help you choose relevant capabilities when building your workflows. +Qiskit addons are a collection of research capabilities for enabling algorithm discovery at the utility scale. These modular software components build on Qiskit’s performant foundation and can plug into a [workflow](/docs/guides/intro-to-patterns) to scale or design new quantum algorithms. This page highlights the available tools across key functional categories to help you choose relevant capabilities when building your workflows. ## Map domain problems @@ -16,7 +16,7 @@ These capabilities specialize in mapping domain problems into quantum operators @@ -32,7 +32,7 @@ These capabilities specialize in mapping domain problems into quantum operators @@ -40,7 +40,7 @@ These capabilities specialize in mapping domain problems into quantum operators @@ -54,7 +54,7 @@ These capabilities are useful for reducing circuit depth and typically come with @@ -62,7 +62,7 @@ These capabilities are useful for reducing circuit depth and typically come with @@ -75,14 +75,14 @@ Use the following addons to manage noise when building quantum workloads that es @@ -97,7 +97,7 @@ These techniques are useful for managing noise on sampling results. @@ -113,15 +113,15 @@ These techniques are useful for managing noise on sampling results. @@ -135,15 +135,15 @@ Use these capabilities to support and compose your workflows that leverage other diff --git a/docs/guides/directed-execution-model.mdx b/docs/guides/directed-execution-model.mdx index 0cc48c4729b..a978418391f 100644 --- a/docs/guides/directed-execution-model.mdx +++ b/docs/guides/directed-execution-model.mdx @@ -69,5 +69,5 @@ To learn more about NoiseLearnerV3, refer to its [API reference](https://qiskit. ## Next steps -- Check out two Qiskit addons, [Shaded lightcones](https://qiskit.github.io/qiskit-addon-slc/) and [Propagated noise absorption](https://github.com/Qiskit/qiskit-addon-pna), which are built on top of this execution model. +- Check out two Qiskit addons, [Shaded lightcones](/docs/addons/qiskit-addon-slc/) and [Propagated noise absorption](/docs/addons/qiskit-addon-pna), which are built on top of this execution model. \ No newline at end of file diff --git a/docs/guides/error-mitigation-overview.mdx b/docs/guides/error-mitigation-overview.mdx index ef75a9a63bc..f5744514f13 100644 --- a/docs/guides/error-mitigation-overview.mdx +++ b/docs/guides/error-mitigation-overview.mdx @@ -40,7 +40,7 @@ When executing quantum workloads, there are multiple ways to reduce the impact o @@ -73,7 +73,7 @@ When executing quantum workloads, there are multiple ways to reduce the impact o @@ -81,21 +81,21 @@ When executing quantum workloads, there are multiple ways to reduce the impact o @@ -107,7 +107,7 @@ When executing quantum workloads, there are multiple ways to reduce the impact o @@ -123,7 +123,7 @@ When executing quantum workloads, there are multiple ways to reduce the impact o diff --git a/docs/guides/latest-updates.mdx b/docs/guides/latest-updates.mdx index f4b6bc75456..174c9184242 100644 --- a/docs/guides/latest-updates.mdx +++ b/docs/guides/latest-updates.mdx @@ -314,9 +314,9 @@ Improvements include the following: - We recently released two new addons that use classical resources to reduce the quantum resources needed for error mitigation: - - [Propagated noise absorption (PNA)](https://qiskit.github.io/qiskit-addon-pna/) is a technique for mitigating errors in expectation values by absorbing the inverses of the learned noise channels into observables using Pauli propagation. + - [Propagated noise absorption (PNA)](/docs/addons/qiskit-addon-pna/) is a technique for mitigating errors in expectation values by absorbing the inverses of the learned noise channels into observables using Pauli propagation. - - The [Shaded lightcones (SLC)](https://qiskit.github.io/qiskit-addon-slc/) addon uses classical simulations to more tightly bound the sensitivity to errors throughout the circuit. This allows for more efficient, targeted applications of Probabilistic Error Cancellation (PEC) with reduced variance. + - The [Shaded lightcones (SLC)](/docs/addons/qiskit-addon-slc/) addon uses classical simulations to more tightly bound the sensitivity to errors throughout the circuit. This allows for more efficient, targeted applications of Probabilistic Error Cancellation (PEC) with reduced variance. ## Qiskit Functions diff --git a/docs/guides/qiskit-function-templates.mdx b/docs/guides/qiskit-function-templates.mdx index f0cf2b5abaa..da1dc1daa0f 100644 --- a/docs/guides/qiskit-function-templates.mdx +++ b/docs/guides/qiskit-function-templates.mdx @@ -15,7 +15,7 @@ There are two types of templates: ## Template implementations -Qiskit Function template implementations are organized by application area. Currently included in the collection is a physics template for Hamiltonian simulation using the [AQC-Tensor Qiskit addon](https://qiskit.github.io/qiskit-addon-aqc-tensor/) and a chemistry template for electronic structure with the implicit solvent model using the [SQD Qiskit addon](/docs/guides/qiskit-addons-sqd). +Qiskit Function template implementations are organized by application area. Currently included in the collection is a physics template for Hamiltonian simulation using the [AQC-Tensor Qiskit addon](/docs/addons/qiskit-addon-aqc-tensor/) and a chemistry template for electronic structure with the implicit solvent model using the [SQD Qiskit addon](/docs/guides/qiskit-addons-sqd). Resources to get started with these two templates are available at the following links: - Electronic structure simulation with implicit solvent model: [template source files](https://github.com/qiskit-community/qiskit-function-templates/tree/main/chemistry/sqd_pcm) and [guide](/docs/guides/function-template-chemistry-workflow) diff --git a/docs/ruff.toml b/docs/ruff.toml index 50ec7749e1f..71708e929c0 100644 --- a/docs/ruff.toml +++ b/docs/ruff.toml @@ -1 +1,2 @@ line-length=78 +exclude = ["addons"] diff --git a/package.json b/package.json index fd0672cd084..55c889bb2f3 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "check:qiskit-versions": "tsx scripts/js/commands/checkQiskitApiVersions.ts", "regen-apis": "tsx scripts/js/commands/api/regenerateApiDocs.ts", "gen-api": "tsx scripts/js/commands/api/updateApiDocs.ts", + "gen-addon": "tsx scripts/js/commands/api/updateAddonDocs.ts", + "postgen-addon": "./fix", "generate-historical-redirects": "tsx scripts/js/commands/api/generateHistoricalRedirects.ts", "save-internal-links": "tsx scripts/js/commands/saveInternalLinks.ts" }, diff --git a/public/docs/api/pauli-prop/objects.inv b/public/docs/api/pauli-prop/objects.inv new file mode 100644 index 00000000000..a985af1a9fb Binary files /dev/null and b/public/docs/api/pauli-prop/objects.inv differ diff --git a/public/docs/api/qiskit-addon-aqc-tensor/objects.inv b/public/docs/api/qiskit-addon-aqc-tensor/objects.inv index 8565dd1054f..f557deaa844 100644 Binary files a/public/docs/api/qiskit-addon-aqc-tensor/objects.inv and b/public/docs/api/qiskit-addon-aqc-tensor/objects.inv differ diff --git a/public/docs/api/qiskit-addon-cutting/objects.inv b/public/docs/api/qiskit-addon-cutting/objects.inv index 0282f2a012e..0f82a840aa1 100644 Binary files a/public/docs/api/qiskit-addon-cutting/objects.inv and b/public/docs/api/qiskit-addon-cutting/objects.inv differ diff --git a/public/docs/api/qiskit-addon-mpf/objects.inv b/public/docs/api/qiskit-addon-mpf/objects.inv index 7eff45d1f48..1a67572a125 100644 Binary files a/public/docs/api/qiskit-addon-mpf/objects.inv and b/public/docs/api/qiskit-addon-mpf/objects.inv differ diff --git a/public/docs/api/qiskit-addon-mthree/objects.inv b/public/docs/api/qiskit-addon-mthree/objects.inv new file mode 100644 index 00000000000..305e2294de8 Binary files /dev/null and b/public/docs/api/qiskit-addon-mthree/objects.inv differ diff --git a/public/docs/api/qiskit-addon-obp/objects.inv b/public/docs/api/qiskit-addon-obp/objects.inv index c5791a33f25..830013f9fbb 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/api/qiskit-addon-opt-mapper/objects.inv b/public/docs/api/qiskit-addon-opt-mapper/objects.inv new file mode 100644 index 00000000000..28ed938f47e Binary files /dev/null and b/public/docs/api/qiskit-addon-opt-mapper/objects.inv differ diff --git a/public/docs/api/qiskit-addon-pna/objects.inv b/public/docs/api/qiskit-addon-pna/objects.inv new file mode 100644 index 00000000000..e250f2353f0 --- /dev/null +++ b/public/docs/api/qiskit-addon-pna/objects.inv @@ -0,0 +1,6 @@ +# Sphinx inventory version 2 +# Project: Qiskit addon: Propagated noise absorption +# Version: +# The remainder of this file is compressed using zlib. +xڭn0D=ʕ^*Kjulڿp҈Ѽɢs* Z;WhhG1O`~`$0Hvlj[L,:R8`ёKX%T 촉 GVԁYB\ay@[ +$ ݀̆qo2_*H[LXfSwj5c;> jT9f&5q >Fy{ \ No newline at end of file diff --git a/public/docs/api/qiskit-addon-slc/objects.inv b/public/docs/api/qiskit-addon-slc/objects.inv new file mode 100644 index 00000000000..3b719c801c6 Binary files /dev/null and b/public/docs/api/qiskit-addon-slc/objects.inv differ diff --git a/public/docs/api/qiskit-addon-sqd-hpc/objects.inv b/public/docs/api/qiskit-addon-sqd-hpc/objects.inv new file mode 100644 index 00000000000..8d96070f457 Binary files /dev/null and b/public/docs/api/qiskit-addon-sqd-hpc/objects.inv differ diff --git a/public/docs/api/qiskit-addon-sqd/objects.inv b/public/docs/api/qiskit-addon-sqd/objects.inv index da335cc5086..053318dcded 100644 Binary files a/public/docs/api/qiskit-addon-sqd/objects.inv and b/public/docs/api/qiskit-addon-sqd/objects.inv differ diff --git a/public/docs/api/qiskit-addon-utils/objects.inv b/public/docs/api/qiskit-addon-utils/objects.inv index b4ff59622be..eb2ccfeb7ff 100644 Binary files a/public/docs/api/qiskit-addon-utils/objects.inv and b/public/docs/api/qiskit-addon-utils/objects.inv differ diff --git a/public/docs/api/qiskit-paulice/objects.inv b/public/docs/api/qiskit-paulice/objects.inv new file mode 100644 index 00000000000..5f43da9b759 Binary files /dev/null and b/public/docs/api/qiskit-paulice/objects.inv differ diff --git a/public/docs/images/addons/pauli-prop/nbsphinx-no-thumbnail.svg b/public/docs/images/addons/pauli-prop/nbsphinx-no-thumbnail.svg new file mode 100644 index 00000000000..9dca7588fa5 --- /dev/null +++ b/public/docs/images/addons/pauli-prop/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/public/docs/images/addons/qiskit-addon-aqc-tensor/aqc-compression.avif b/public/docs/images/addons/qiskit-addon-aqc-tensor/aqc-compression.avif new file mode 100644 index 00000000000..729a6dfbf2e Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-aqc-tensor/aqc-compression.avif differ diff --git a/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer/extracted-outputs/5d07640a-d7ee-4966-80e8-dd084ffb0da3-0.avif b/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer/extracted-outputs/5d07640a-d7ee-4966-80e8-dd084ffb0da3-0.avif new file mode 100644 index 00000000000..98e2acfee13 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos/01-quimb-tnoptimizer/extracted-outputs/5d07640a-d7ee-4966-80e8-dd084ffb0da3-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos_01_quimb_tnoptimizer_7_0.avif b/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos_01_quimb_tnoptimizer_7_0.avif new file mode 100644 index 00000000000..98e2acfee13 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-aqc-tensor/how-tos_01_quimb_tnoptimizer_7_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/44956cbb-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/44956cbb-0.avif new file mode 100644 index 00000000000..573b2332ad6 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/44956cbb-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/d4ccf5b8-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/d4ccf5b8-0.avif new file mode 100644 index 00000000000..d03d4e44b5e Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/d4ccf5b8-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/dd147239-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/dd147239-0.avif new file mode 100644 index 00000000000..02fd34d4350 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients/extracted-outputs/dd147239-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables/extracted-outputs/5defa9fb-e928-4fcb-9ae3-bed8e8f2527b-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables/extracted-outputs/5defa9fb-e928-4fcb-9ae3-bed8e8f2527b-0.avif new file mode 100644 index 00000000000..dc4133dc92a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-provide-multiple-observables/extracted-outputs/5defa9fb-e928-4fcb-9ae3-bed8e8f2527b-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/0ae22516-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/0ae22516-0.avif new file mode 100644 index 00000000000..211ace31c5a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/0ae22516-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/631286a6-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/631286a6-0.avif new file mode 100644 index 00000000000..f6bf05eafaf Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/631286a6-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/975a3ca9-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/975a3ca9-0.avif new file mode 100644 index 00000000000..ca2c92dccf6 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/975a3ca9-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/aaef5b3d-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/aaef5b3d-0.avif new file mode 100644 index 00000000000..8cf1ce24529 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/aaef5b3d-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/e4ee1559-0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/e4ee1559-0.avif new file mode 100644 index 00000000000..918c5622977 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires/extracted-outputs/e4ee1559-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_generate_exact_sampling_coefficients_5_0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_generate_exact_sampling_coefficients_5_0.avif new file mode 100644 index 00000000000..573b2332ad6 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_generate_exact_sampling_coefficients_5_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_provide_multiple_observables_2_0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_provide_multiple_observables_2_0.avif new file mode 100644 index 00000000000..dc4133dc92a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_provide_multiple_observables_2_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_specify_cut_wires_18_0.avif b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_specify_cut_wires_18_0.avif new file mode 100644 index 00000000000..ca2c92dccf6 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-cutting/how-tos_how_to_specify_cut_wires_18_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-cutting/index-1.svg b/public/docs/images/addons/qiskit-addon-cutting/index-1.svg new file mode 100644 index 00000000000..54eda97e5d2 --- /dev/null +++ b/public/docs/images/addons/qiskit-addon-cutting/index-1.svg @@ -0,0 +1,2865 @@ + + + + + + + + 2025-03-28T16:50:49.198051 + image/svg+xml + + + Matplotlib v3.9.4, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/docs/images/addons/qiskit-addon-cutting/nbsphinx-no-thumbnail.svg b/public/docs/images/addons/qiskit-addon-cutting/nbsphinx-no-thumbnail.svg new file mode 100644 index 00000000000..9dca7588fa5 --- /dev/null +++ b/public/docs/images/addons/qiskit-addon-cutting/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/public/docs/images/addons/qiskit-addon-mpf/explanations/mpf-stability/extracted-outputs/845cf113-58da-454e-97b6-679d5a5ae9fb-0.avif b/public/docs/images/addons/qiskit-addon-mpf/explanations/mpf-stability/extracted-outputs/845cf113-58da-454e-97b6-679d5a5ae9fb-0.avif new file mode 100644 index 00000000000..289bf7e1f25 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mpf/explanations/mpf-stability/extracted-outputs/845cf113-58da-454e-97b6-679d5a5ae9fb-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/basic_0_1.avif b/public/docs/images/addons/qiskit-addon-mthree/basic_0_1.avif new file mode 100644 index 00000000000..88c8ddfc723 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/basic_0_1.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/error_0_0.avif b/public/docs/images/addons/qiskit-addon-mthree/error_0_0.avif new file mode 100644 index 00000000000..88c8ddfc723 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/error_0_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/grouped_0_0.avif b/public/docs/images/addons/qiskit-addon-mthree/grouped_0_0.avif new file mode 100644 index 00000000000..a4cabcbec9c Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/grouped_0_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/marginals_0_0.avif b/public/docs/images/addons/qiskit-addon-mthree/marginals_0_0.avif new file mode 100644 index 00000000000..d4d0ebf66d7 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/marginals_0_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/sampling/extracted-outputs/40654bec-0.avif b/public/docs/images/addons/qiskit-addon-mthree/sampling/extracted-outputs/40654bec-0.avif new file mode 100644 index 00000000000..9ccc48e5e90 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/sampling/extracted-outputs/40654bec-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/transpiled_0_0.avif b/public/docs/images/addons/qiskit-addon-mthree/transpiled_0_0.avif new file mode 100644 index 00000000000..eb6f440102c Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/transpiled_0_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/transpiled_1_0.avif b/public/docs/images/addons/qiskit-addon-mthree/transpiled_1_0.avif new file mode 100644 index 00000000000..29db3c9ae81 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/transpiled_1_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/truncation.avif b/public/docs/images/addons/qiskit-addon-mthree/truncation.avif new file mode 100644 index 00000000000..9a929ef9562 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/truncation.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/utils_0_0.avif b/public/docs/images/addons/qiskit-addon-mthree/utils_0_0.avif new file mode 100644 index 00000000000..cf5b7a95e11 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/utils_0_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-mthree/utils_1_0.avif b/public/docs/images/addons/qiskit-addon-mthree/utils_1_0.avif new file mode 100644 index 00000000000..df123d08460 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-mthree/utils_1_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/bound-error-using-p-norm/extracted-outputs/d6f281d4-706e-41ad-b7b5-bc7ebe106996-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/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/how_tos/bound-error-using-p-norm/extracted-outputs/fe7f454c-7d79-4c61-b1dd-6a98564fb5f1-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/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/how_tos/bound-error-using-p-norm/extracted-outputs/ff72e77f-e2c0-4cce-8575-c5aa587f78fb-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/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/how_tos/simulating-circuits-with-obp/extracted-outputs/8758f393-8691-4116-94ca-1185c6188bb4-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/simulating-circuits-with-obp/extracted-outputs/8758f393-8691-4116-94ca-1185c6188bb4-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp/extracted-outputs/dadc314d-452f-465c-a382-d54685ce3f2a-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/simulating-circuits-with-obp/extracted-outputs/dadc314d-452f-465c-a382-d54685ce3f2a-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-obp/how_tos/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif b/public/docs/images/addons/qiskit-addon-obp/how_tos/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/how_tos/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/27b1b8d3-e2b7-4304-b8d8-d5e63af5deec-0.avif b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/27b1b8d3-e2b7-4304-b8d8-d5e63af5deec-0.avif new file mode 100644 index 00000000000..7fe8dbd32bc Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/27b1b8d3-e2b7-4304-b8d8-d5e63af5deec-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/309f88e4-5d1f-47f8-b63a-50346829ffeb-0.avif b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/309f88e4-5d1f-47f8-b63a-50346829ffeb-0.avif new file mode 100644 index 00000000000..9ef8d21f012 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/309f88e4-5d1f-47f8-b63a-50346829ffeb-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/f1281552-b611-4911-bdb9-df7d1df97ad4-0.avif b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/f1281552-b611-4911-bdb9-df7d1df97ad4-0.avif new file mode 100644 index 00000000000..b25cd55bcf4 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-opt-mapper/how_tos/03-validate-with-solvers/extracted-outputs/f1281552-b611-4911-bdb9-df7d1df97ad4-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-pna/nbsphinx-no-thumbnail.svg b/public/docs/images/addons/qiskit-addon-pna/nbsphinx-no-thumbnail.svg new file mode 100644 index 00000000000..9dca7588fa5 --- /dev/null +++ b/public/docs/images/addons/qiskit-addon-pna/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/public/docs/images/addons/qiskit-addon-pna/noise_mitigated_expt.avif b/public/docs/images/addons/qiskit-addon-pna/noise_mitigated_expt.avif new file mode 100644 index 00000000000..5e86cec14fd Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-pna/noise_mitigated_expt.avif differ diff --git a/public/docs/images/addons/qiskit-addon-pna/noisy_expt.avif b/public/docs/images/addons/qiskit-addon-pna/noisy_expt.avif new file mode 100644 index 00000000000..9728c94d57f Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-pna/noisy_expt.avif differ diff --git a/public/docs/images/addons/qiskit-addon-pna/pna_overview.avif b/public/docs/images/addons/qiskit-addon-pna/pna_overview.avif new file mode 100644 index 00000000000..d74fe9a6864 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-pna/pna_overview.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/6b961c81-0.avif b/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/6b961c81-0.avif new file mode 100644 index 00000000000..50e80759124 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/6b961c81-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/9abb110f-0.avif b/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/9abb110f-0.avif new file mode 100644 index 00000000000..f71d718c740 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-sqd/how_tos/benchmark-pauli-projection/extracted-outputs/9abb110f-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension/extracted-outputs/caffd888-e89c-4aa9-8bae-4d1bb723b35e-0.avif b/public/docs/images/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension/extracted-outputs/caffd888-e89c-4aa9-8bae-4d1bb723b35e-0.avif new file mode 100644 index 00000000000..d7bc5a1192a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-sqd/how_tos/choose-subspace-dimension/extracted-outputs/caffd888-e89c-4aa9-8bae-4d1bb723b35e-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/how_tos_benchmark_pauli_projection_12_0.avif b/public/docs/images/addons/qiskit-addon-sqd/how_tos_benchmark_pauli_projection_12_0.avif new file mode 100644 index 00000000000..50e80759124 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-sqd/how_tos_benchmark_pauli_projection_12_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/how_tos_choose_subspace_dimension_8_0.avif b/public/docs/images/addons/qiskit-addon-sqd/how_tos_choose_subspace_dimension_8_0.avif new file mode 100644 index 00000000000..d7bc5a1192a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-sqd/how_tos_choose_subspace_dimension_8_0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg b/public/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg new file mode 100644 index 00000000000..9dca7588fa5 --- /dev/null +++ b/public/docs/images/addons/qiskit-addon-sqd/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/03a5f389-94c0-4496-9fe6-262dca62a0e3-1.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/03a5f389-94c0-4496-9fe6-262dca62a0e3-1.avif new file mode 100644 index 00000000000..c7509568f7a Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/03a5f389-94c0-4496-9fe6-262dca62a0e3-1.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/2806ec31-05e8-4a4c-832b-888150506191-1.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/2806ec31-05e8-4a4c-832b-888150506191-1.avif new file mode 100644 index 00000000000..0e39125fed9 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/2806ec31-05e8-4a4c-832b-888150506191-1.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/9b986eac-93c6-453a-bfe1-2546681ab38b-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/9b986eac-93c6-453a-bfe1-2546681ab38b-0.avif new file mode 100644 index 00000000000..311adfef85c Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/9b986eac-93c6-453a-bfe1-2546681ab38b-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/c3f482a1-6868-4cda-b8ed-8a8489794707-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/c3f482a1-6868-4cda-b8ed-8a8489794707-0.avif new file mode 100644 index 00000000000..c45bcab464e Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/color-device-edges-to-improve-depth/extracted-outputs/c3f482a1-6868-4cda-b8ed-8a8489794707-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/110b5844-972e-46d1-bfc5-20a30b69f814-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/110b5844-972e-46d1-bfc5-20a30b69f814-0.avif new file mode 100644 index 00000000000..a2204a9eea8 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/110b5844-972e-46d1-bfc5-20a30b69f814-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/1a5d5aff-f988-4f14-ad25-9f0105202b77-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/1a5d5aff-f988-4f14-ad25-9f0105202b77-0.avif new file mode 100644 index 00000000000..0d5a240702c Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/1a5d5aff-f988-4f14-ad25-9f0105202b77-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/791bd07b-739a-45ec-a3e9-2d068f8bd960-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/791bd07b-739a-45ec-a3e9-2d068f8bd960-0.avif new file mode 100644 index 00000000000..8ec7afe076c Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/791bd07b-739a-45ec-a3e9-2d068f8bd960-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/948aa715-22e8-41e1-943d-de7eb0aa379c-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/948aa715-22e8-41e1-943d-de7eb0aa379c-0.avif new file mode 100644 index 00000000000..61cf4299d12 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/948aa715-22e8-41e1-943d-de7eb0aa379c-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9da8d173-8ce9-47a9-a24f-ab32e45f8337-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9da8d173-8ce9-47a9-a24f-ab32e45f8337-0.avif new file mode 100644 index 00000000000..972add14f61 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9da8d173-8ce9-47a9-a24f-ab32e45f8337-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9f766d69-e10c-4aa8-a3bc-2057dab7a69a-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9f766d69-e10c-4aa8-a3bc-2057dab7a69a-0.avif new file mode 100644 index 00000000000..987ea6af65f Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/9f766d69-e10c-4aa8-a3bc-2057dab7a69a-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/a00c8743-f3b9-460c-ba2c-ffc634e99e0e-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/a00c8743-f3b9-460c-ba2c-ffc634e99e0e-0.avif new file mode 100644 index 00000000000..044488f7c26 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/a00c8743-f3b9-460c-ba2c-ffc634e99e0e-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/bc5ebecb-6c8b-4f51-8199-b236a0ba3328-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/bc5ebecb-6c8b-4f51-8199-b236a0ba3328-0.avif new file mode 100644 index 00000000000..d40018ce2fc Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/bc5ebecb-6c8b-4f51-8199-b236a0ba3328-0.avif differ diff --git a/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/f883fa2e-f943-4c49-af45-fb26cadb72b6-0.avif b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/f883fa2e-f943-4c49-af45-fb26cadb72b6-0.avif new file mode 100644 index 00000000000..703822aae56 Binary files /dev/null and b/public/docs/images/addons/qiskit-addon-utils/how_tos/create-circuit-slices/extracted-outputs/f883fa2e-f943-4c49-af45-fb26cadb72b6-0.avif differ diff --git a/public/docs/images/addons/qiskit-paulice/paulice.avif b/public/docs/images/addons/qiskit-paulice/paulice.avif new file mode 100644 index 00000000000..247e21bc323 Binary files /dev/null and b/public/docs/images/addons/qiskit-paulice/paulice.avif differ diff --git a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-2.avif b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-2.avif index e4caf042c3e..63c42da97b9 100644 Binary files a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-2.avif and b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-2.avif differ diff --git a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-3.avif b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-3.avif index 13f204e5174..e5e51e8ee83 100644 Binary files a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-3.avif and b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-quimb_layers-3.avif differ diff --git a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-2.avif b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-2.avif index e4caf042c3e..63c42da97b9 100644 Binary files a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-2.avif and b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-2.avif differ diff --git a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-3.avif b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-3.avif index 13f204e5174..e5e51e8ee83 100644 Binary files a/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-3.avif and b/public/docs/images/api/qiskit-addon-mpf/qiskit_addon_mpf-backends-tenpy_layers-3.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/public/docs/images/api/qiskit-addon-utils/qiskit_addon_utils-problem_generators-1.avif b/public/docs/images/api/qiskit-addon-utils/qiskit_addon_utils-problem_generators-1.avif index 7b18ec134a3..35418995a51 100644 Binary files a/public/docs/images/api/qiskit-addon-utils/qiskit_addon_utils-problem_generators-1.avif and b/public/docs/images/api/qiskit-addon-utils/qiskit_addon_utils-problem_generators-1.avif differ diff --git a/scripts/ci/check-all-notebooks-are-tested.py b/scripts/ci/check-all-notebooks-are-tested.py index e10e3a3f5de..99704e70df5 100644 --- a/scripts/ci/check-all-notebooks-are-tested.py +++ b/scripts/ci/check-all-notebooks-are-tested.py @@ -15,6 +15,7 @@ they don't slip through our CI tests. """ +import fnmatch from pathlib import Path import tomllib import sys @@ -22,15 +23,21 @@ config_path = Path("scripts/config/notebook-testing.toml") config = tomllib.loads(config_path.read_text()) -categorized_notebooks = set() +categorized_notebooks: set[Path] = set() +categorized_globs: list[str] = [] for group in config["groups"].values(): for path in group.get("notebooks", []): - categorized_notebooks.add(Path(path)) + if "*" in path or "?" in path: + categorized_globs.append(path) + else: + categorized_notebooks.add(Path(path)) uncategorized = [ path for path in Path(".").glob("[!.]*/**/*.ipynb") - if not path.match("**/.ipynb_checkpoints/**") and path not in categorized_notebooks + if not path.match("**/.ipynb_checkpoints/**") + and path not in categorized_notebooks + and not any(fnmatch.fnmatch(str(path), g) for g in categorized_globs) ] if uncategorized: diff --git a/scripts/config/api-html-artifacts.json b/scripts/config/api-html-artifacts.json index 831d30a3227..8f644bc7dc5 100644 --- a/scripts/config/api-html-artifacts.json +++ b/scripts/config/api-html-artifacts.json @@ -121,5 +121,29 @@ "0.3": "https://ibm.box.com/shared/static/921yb7b7162e20a9b7ojgzhc1erxhgzd.zip", "0.2": "https://ibm.box.com/shared/static/61qpikp70xgftx1gjs8an0cxc3196ais.zip", "0.1": "https://ibm.box.com/shared/static/6kzfxqaigskwfd8uf9dldrwqtd5yr5fd.zip" + }, + "qiskit-addon-sqd-hpc": { + "0.0": "https://ibm.box.com/shared/static/hktx0wsre5rc7zgzbh3yz2nmi8y5jre0.zip" + }, + "qiskit-addon-mthree": { + "3.0": "https://ibm.box.com/shared/static/ex9xuuuv2xo99pttfjis82hnb5xvwgye.zip" + }, + "qiskit-addon-pna": { + "0.2": "https://ibm.box.com/shared/static/9wxwvchyal0oqkbe46a13d9wzwxi8wxd.zip" + }, + "qiskit-addon-slc": { + "0.1": "https://ibm.box.com/shared/static/87gic8sr1vc85y4g6g1h0x6gk1eb1sqw.zip" + }, + "qiskit-addon-opt-mapper": { + "0.1": "https://ibm.box.com/shared/static/gdjk8grpxrjno8m32ft9lw6j7h40j11a.zip" + }, + "qiskit-fermions": { + "0.0": "https://ibm.box.com/shared/static/xt6apdhkq6f653w6ck0247etidll0osz.zip" + }, + "qiskit-paulice": { + "0.1": "https://ibm.box.com/shared/static/zo45lb3417dnh3kjf71ph6ummu7lztqb.zip" + }, + "pauli-prop": { + "0.2": "https://ibm.box.com/shared/static/lfl06p9hvlo4890o0lx2dheiebf82msv.zip" } } diff --git a/scripts/config/cspell/cSpell.json b/scripts/config/cspell/cSpell.json index 4504eb48de6..347e1b4ef0c 100644 --- a/scripts/config/cspell/cSpell.json +++ b/scripts/config/cspell/cSpell.json @@ -43,7 +43,18 @@ "Teleporting", "ebitsa", "ebitsb", - "pmzero" + "pmzero", + "textrm", + "Frontmatter", + "leftarrow", + "forall", + "arxiv", + "subpace", // fix in addon-sqd + "behaviour", // fix in addon-sqd + "susbapce", // fix in addon-sqd + "Counfiguration", // fix in addon-sqd + "construnct", // fix in addon-sqd + "rigourous" // fix in addon-mthree ], "ignoreRegExpList": [ // Markdown links diff --git a/scripts/config/cspell/dictionaries/people.txt b/scripts/config/cspell/dictionaries/people.txt index 974982241ec..2d809fa5305 100644 --- a/scripts/config/cspell/dictionaries/people.txt +++ b/scripts/config/cspell/dictionaries/people.txt @@ -332,3 +332,5 @@ Smithline Greenberger Gaolis Bharti +Minkowski +Carrera \ No newline at end of file diff --git a/scripts/config/cspell/dictionaries/qiskit.txt b/scripts/config/cspell/dictionaries/qiskit.txt index 7939b1a2075..00d982f5aaf 100644 --- a/scripts/config/cspell/dictionaries/qiskit.txt +++ b/scripts/config/cspell/dictionaries/qiskit.txt @@ -449,3 +449,21 @@ mdash postselect postselected postselecting +circo +backprop +ncols +CLARABEL +walltime +hcore +SQSD +milli +fontdict +fontsize +Utar +nocc +tnopt +quasis +Milp +lowerbound +upperbound +HUBO \ No newline at end of file diff --git a/scripts/config/historical-pages-to-latest.json b/scripts/config/historical-pages-to-latest.json index d0a88e62787..c5564e917c6 100644 --- a/scripts/config/historical-pages-to-latest.json +++ b/scripts/config/historical-pages-to-latest.json @@ -1471,6 +1471,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": {} @@ -1499,15 +1510,10 @@ "noise-management-post-selection": "/" } }, - "qiskit-c": { - "2.0": {}, - "2.1": {}, - "2.2": { - "qk-vf-2-layout-result": "/" - }, - "2.3": {}, - "dev": { - "qk-param": "/" - } - } + "qiskit-addon-mthree": {}, + "qiskit-addon-pna": {}, + "qiskit-addon-slc": {}, + "qiskit-addon-opt-mapper": {}, + "qiskit-paulice": {}, + "pauli-prop": {} } diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml index 6d5c4fa22ac..889e36127c4 100644 --- a/scripts/config/notebook-testing.toml +++ b/scripts/config/notebook-testing.toml @@ -138,6 +138,12 @@ notebooks = [ # Don't ever test the following notebooks [groups.exclude] notebooks = [ + # Addon notebooks require the addon packages and are not run in standard CI + "docs/addons/**/*.ipynb", + + # Test fixture — not real content + "scripts/js/lib/api/testdata/**/*.ipynb", + # This notebook contains undefined variables so can't run at all. "docs/guides/function-template-hamiltonian-simulation.ipynb", "docs/guides/function-template-chemistry-workflow.ipynb", diff --git a/scripts/js/commands/api/updateAddonDocs.ts b/scripts/js/commands/api/updateAddonDocs.ts new file mode 100644 index 00000000000..00e467e48dc --- /dev/null +++ b/scripts/js/commands/api/updateAddonDocs.ts @@ -0,0 +1,106 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2024. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +import { readdir } from "fs/promises"; + +import yargs from "yargs/yargs"; +import { hideBin } from "yargs/helpers"; + +import { Pkg } from "../../lib/api/Pkg.js"; +import { runAddonDocsPipeline } from "../../lib/api/addonDocsPipeline.js"; +import { zxMain } from "../../lib/zx.js"; +import { parseMinorVersion } from "../../lib/apiVersions.js"; +import { deleteOutputDirs, prepareSphinxFolder } from "./updateDocsShared.js"; + +type Args = { + package?: string; + all: boolean; + skipDownload: boolean; + sphinxArtifactFolder?: string; +}; + +const readArgs = (): Args => { + return yargs(hideBin(process.argv)) + .version(false) + .option("package", { + alias: "p", + type: "string", + choices: Pkg.ADDON_NAMES, + description: "Which addon package to update", + }) + .option("all", { + type: "boolean", + default: false, + description: "Update all addon packages in parallel", + }) + .option("skip-download", { + alias: "s", + type: "boolean", + default: false, + description: "Skip downloading the Sphinx artifact", + }) + .option("sphinx-artifact-folder", { + type: "string", + description: "Use a local artifact folder instead of downloading", + }) + .check((argv) => { + if (!argv.all && !argv.package) { + throw new Error("Either --package or --all is required"); + } + return true; + }) + .parseSync() as unknown as Args; +}; + +async function resolveVersion(pkgName: string): Promise { + const entries = await readdir(`.sphinx-artifacts/${pkgName}`); + const versions = entries.filter((e) => /^\d/.test(e)).sort(); + if (versions.length === 0) { + throw new Error( + `No artifact versions found for ${pkgName}. Run without --skip-download first.`, + ); + } + return versions.at(-1)!; +} + +async function generateVersion(pkg: Pkg, args: Args): Promise { + const sphinxArtifactFolder = await prepareSphinxFolder(pkg, args); + await deleteOutputDirs(pkg, { + markdownDir: pkg.outputDir("docs/addons"), + imagesDir: pkg.outputDir("public/docs/images/addons"), + recursive: true, + }); + + console.log(`Run pipeline for ${pkg.name}:${pkg.versionWithoutPatch}`); + await runAddonDocsPipeline( + sphinxArtifactFolder, + "docs/addons", + "public/docs", + pkg, + ); +} + +async function generatePackage(pkgName: string, args: Args): Promise { + const version = await resolveVersion(pkgName); + const minorVersion = parseMinorVersion(version); + if (minorVersion === null) { + throw new Error(`Could not parse version ${version} for ${pkgName}`); + } + const pkg = await Pkg.fromArgs(pkgName, version, minorVersion, "latest"); + await generateVersion(pkg, args); +} + +zxMain(async () => { + const args = readArgs(); + const packages = args.all ? Pkg.ADDON_NAMES : [args.package!]; + await Promise.all(packages.map((pkgName) => generatePackage(pkgName, args))); +}); diff --git a/scripts/js/commands/api/updateApiDocs.ts b/scripts/js/commands/api/updateApiDocs.ts index edac31e251b..08d350e6f73 100644 --- a/scripts/js/commands/api/updateApiDocs.ts +++ b/scripts/js/commands/api/updateApiDocs.ts @@ -15,11 +15,10 @@ import { hideBin } from "yargs/helpers"; import { Pkg } from "../../lib/api/Pkg.js"; import { zxMain } from "../../lib/zx.js"; -import { parseMinorVersion, isValidVersion } from "../../lib/apiVersions.js"; -import { pathExists, rmFilesInFolder } from "../../lib/fs.js"; -import { downloadSphinxArtifact } from "../../lib/api/sphinxArtifacts.js"; -import { runConversionPipeline } from "../../lib/api/conversionPipeline.js"; +import { isValidVersion, parseMinorVersion } from "../../lib/apiVersions.js"; +import { runApiDocsPipeline } from "../../lib/api/apiDocsPipeline.js"; import { generateHistoricalRedirects } from "./generateHistoricalRedirects.js"; +import { deleteOutputDirs, prepareSphinxFolder } from "./updateDocsShared.js"; export interface Arguments { [x: string]: unknown; @@ -87,10 +86,14 @@ export async function generateVersion( args: Arguments, ): Promise { const sphinxArtifactFolder = await prepareSphinxFolder(pkg, args); - await deleteExistingFiles(pkg); + await deleteOutputDirs(pkg, { + markdownDir: pkg.apiOutputDir("docs"), + imagesDir: pkg.apiOutputDir("public/docs/images"), + recursive: false, + }); console.log(`Run pipeline for ${pkg.name}:${pkg.versionWithoutPatch}`); - await runConversionPipeline(sphinxArtifactFolder, "docs", "public/docs", pkg); + await runApiDocsPipeline(sphinxArtifactFolder, "docs", "public/docs", pkg); await generateHistoricalRedirects(); } @@ -112,43 +115,6 @@ export function determineMinorVersion(args: Arguments): string { return minorVersion; } -async function prepareSphinxFolder(pkg: Pkg, args: Arguments): Promise { - if (args.sphinxArtifactFolder) { - if (!(await pathExists(args.sphinxArtifactFolder))) { - throw new Error( - `Explicit artifact path '${args.sphinxArtifactFolder}' does not exist.`, - ); - } - return args.sphinxArtifactFolder; - } - const sphinxArtifactFolder = pkg.sphinxArtifactFolder(); - if ( - args.skipDownload && - (await pathExists(`${sphinxArtifactFolder}/artifact`)) - ) { - console.log( - `Skip downloading sources for ${pkg.name}:${pkg.versionWithoutPatch}`, - ); - } else { - await downloadSphinxArtifact(pkg, sphinxArtifactFolder); - } - return `${sphinxArtifactFolder}/artifact`; -} - -async function deleteExistingFiles(pkg: Pkg): Promise { - const markdownDir = pkg.outputDir("docs"); - if (await pathExists(markdownDir)) { - await rmFilesInFolder(markdownDir); - } - const imagesDir = pkg.outputDir("public/docs/images"); - if (await pathExists(imagesDir)) { - await rmFilesInFolder(imagesDir); - } - console.log( - `Deleted existing markdown & images for ${pkg.name}:${pkg.versionWithoutPatch}`, - ); -} - if (import.meta.url === `file://${process.argv[1]}`) { zxMain(async () => { const args = readArgs(); diff --git a/scripts/js/commands/api/updateDocsShared.ts b/scripts/js/commands/api/updateDocsShared.ts new file mode 100644 index 00000000000..10163ee73d5 --- /dev/null +++ b/scripts/js/commands/api/updateDocsShared.ts @@ -0,0 +1,87 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Shared helpers for updateApiDocs.ts and updateAddonDocs.ts — they both +// take --package/--version, download (or reuse) a Sphinx artifact, and wipe +// an output directory before running a pipeline. + +import { $ } from "zx"; + +import { Pkg } from "../../lib/api/Pkg.js"; +import { pathExists, rmFilesInFolder } from "../../lib/fs.js"; +import { downloadSphinxArtifact } from "../../lib/api/sphinxArtifacts.js"; + +/** + * Resolve the Sphinx artifact folder: either a user-provided path, a reused + * prior download, or a fresh download from Box. + */ +export async function prepareSphinxFolder( + pkg: Pkg, + args: { skipDownload: boolean; sphinxArtifactFolder?: string }, +): Promise { + if (args.sphinxArtifactFolder) { + if (!(await pathExists(args.sphinxArtifactFolder))) { + throw new Error( + `Explicit artifact path '${args.sphinxArtifactFolder}' does not exist.`, + ); + } + return args.sphinxArtifactFolder; + } + const sphinxArtifactFolder = pkg.sphinxArtifactFolder(); + if ( + args.skipDownload && + (await pathExists(`${sphinxArtifactFolder}/artifact`)) + ) { + console.log( + `Skip downloading sources for ${pkg.name}:${pkg.versionWithoutPatch}`, + ); + } else { + await downloadSphinxArtifact(pkg, sphinxArtifactFolder); + } + return `${sphinxArtifactFolder}/artifact`; +} + +export interface DeleteOptions { + /** Markdown output directory (contents removed before pipeline runs). */ + markdownDir: string; + /** Image output directory (removed entirely before pipeline runs). */ + imagesDir: string; + /** + * If true, recursively wipe `markdownDir`; otherwise only delete top-level + * files (so sibling historical-version subfolders are preserved). + */ + recursive: boolean; +} + +/** Wipe output directories in preparation for a fresh pipeline run. */ +export async function deleteOutputDirs( + pkg: Pkg, + { markdownDir, imagesDir, recursive }: DeleteOptions, +): Promise { + if (await pathExists(markdownDir)) { + if (recursive) { + await $`rm -rf ${markdownDir}`; + } else { + await rmFilesInFolder(markdownDir); + } + } + if (await pathExists(imagesDir)) { + if (recursive) { + await $`rm -rf ${imagesDir}`; + } else { + await rmFilesInFolder(imagesDir); + } + } + console.log( + `Deleted existing docs & images for ${pkg.name}:${pkg.versionWithoutPatch}`, + ); +} diff --git a/scripts/js/commands/checkInternalLinks.ts b/scripts/js/commands/checkInternalLinks.ts index e17b21e6fdf..e844ba3d414 100644 --- a/scripts/js/commands/checkInternalLinks.ts +++ b/scripts/js/commands/checkInternalLinks.ts @@ -191,10 +191,53 @@ async function determineFileBatches(args: Arguments): Promise { ADDON_GLOBS_TO_LOAD, { check: args.historicalApis }, ); + const mthree = await determineHistoricalFileBatches( + "qiskit-addon-mthree", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); + const pna = await determineHistoricalFileBatches( + "qiskit-addon-pna", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); + const slc = await determineHistoricalFileBatches( + "qiskit-addon-slc", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); + const optMapper = await determineHistoricalFileBatches( + "qiskit-addon-opt-mapper", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); + const paulice = await determineHistoricalFileBatches( + "qiskit-paulice", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); + const pauliProp = await determineHistoricalFileBatches( + "pauli-prop", + ADDON_GLOBS_TO_LOAD, + { check: args.historicalApis }, + ); // This is intentionally ordered so that the smallest APIs are checked first, // since they are much faster to check. - result.push(...transpiler, ...sqd, ...mpf, ...utils, ...runtime, ...qiskit); + result.push( + ...transpiler, + ...sqd, + ...mpf, + ...utils, + ...mthree, + ...pna, + ...slc, + ...optMapper, + ...paulice, + ...pauliProp, + ...runtime, + ...qiskit, + ); if (args.qiskitLegacyReleaseNotes) { result.push(await determineQiskitLegacyReleaseNotes()); diff --git a/scripts/js/commands/checkQiskitBotFiles.ts b/scripts/js/commands/checkQiskitBotFiles.ts index e3cf1e42ac3..1db1794c6b9 100644 --- a/scripts/js/commands/checkQiskitBotFiles.ts +++ b/scripts/js/commands/checkQiskitBotFiles.ts @@ -51,6 +51,7 @@ const GLOBS = [ "{docs,learning}/**/*.{ipynb,mdx}", "!docs/api/**/*", "docs/api/functions/**", + "!docs/addons/**/*", ]; async function main() { diff --git a/scripts/js/lib/api/HtmlToMdResult.ts b/scripts/js/lib/api/HtmlToMdResult.ts index 4438b23aae2..85b10a92238 100644 --- a/scripts/js/lib/api/HtmlToMdResult.ts +++ b/scripts/js/lib/api/HtmlToMdResult.ts @@ -15,6 +15,8 @@ import { Metadata } from "./Metadata.js"; export type Image = { fileName: string; dest: string; + /** Path to the image relative to the artifact root (e.g. `_static/foo.svg` or `_images/foo.svg`) */ + originSrc: string; }; export type HtmlToMdResult = { diff --git a/scripts/js/lib/api/Notebooks.ts b/scripts/js/lib/api/Notebooks.ts new file mode 100644 index 00000000000..5870dcbc7e2 --- /dev/null +++ b/scripts/js/lib/api/Notebooks.ts @@ -0,0 +1,17 @@ +export type NotebookCell = { + id?: string; + cell_type: "code" | "markdown" | "raw"; + source: string | string[]; + metadata: Record; + outputs?: unknown[]; + execution_count?: number | null; +}; + +export type Notebook = { + nbformat: number; + nbformat_minor: number; + metadata: Record; + cells: NotebookCell[]; +}; + +export type NotebookWithUrl = Notebook & { url: string }; diff --git a/scripts/js/lib/api/Pkg.ts b/scripts/js/lib/api/Pkg.ts index 9b3b1145f86..943b43e51ee 100644 --- a/scripts/js/lib/api/Pkg.ts +++ b/scripts/js/lib/api/Pkg.ts @@ -57,18 +57,32 @@ export class Pkg { readonly kebabCaseAndShortenUrls: boolean; readonly artifactPackageName: string; readonly hasRootNamespaceFile: boolean; + /** Slugs of docs/tutorials/ notebooks to surface under this addon's tutorials route. */ + readonly tutorials: string[]; - static VALID_NAMES = [ - "qiskit", - "qiskit-ibm-runtime", - "qiskit-ibm-transpiler", + static ADDON_NAMES = [ "qiskit-addon-aqc-tensor", "qiskit-addon-obp", "qiskit-addon-mpf", "qiskit-addon-sqd", + // "qiskit-addon-sqd-hpc", "qiskit-addon-cutting", "qiskit-addon-utils", + "qiskit-addon-mthree", + "qiskit-addon-pna", + "qiskit-addon-slc", + "qiskit-addon-opt-mapper", + // "qiskit-fermions", + "qiskit-paulice", + "pauli-prop", + ]; + + static VALID_NAMES = [ + "qiskit", + "qiskit-ibm-runtime", + "qiskit-ibm-transpiler", "qiskit-c", + ...Pkg.ADDON_NAMES, ]; constructor(kwargs: { @@ -84,6 +98,7 @@ export class Pkg { kebabCaseAndShortenUrls: boolean; artifactPackageName?: string; hasRootNamespaceFile?: boolean; + tutorials?: string[]; }) { this.name = kwargs.name; this.title = kwargs.title; @@ -98,6 +113,7 @@ export class Pkg { this.kebabCaseAndShortenUrls = kwargs.kebabCaseAndShortenUrls; this.artifactPackageName = kwargs.artifactPackageName ?? this.name; this.hasRootNamespaceFile = kwargs.hasRootNamespaceFile ?? false; + this.tutorials = kwargs.tutorials ?? []; } static async fromArgs( @@ -162,6 +178,7 @@ export class Pkg { githubSlug: "Qiskit/qiskit-addon-aqc-tensor", kebabCaseAndShortenUrls: true, language: "Python", + tutorials: ["approximate-quantum-compilation-for-time-evolution"], }); } if (name === "qiskit-addon-obp") { @@ -171,6 +188,7 @@ export class Pkg { githubSlug: "Qiskit/qiskit-addon-obp", kebabCaseAndShortenUrls: true, language: "Python", + tutorials: ["operator-back-propagation"], }); } if (name === "qiskit-addon-mpf") { @@ -181,6 +199,7 @@ export class Pkg { kebabCaseAndShortenUrls: true, tocGrouping: QISKIT_ADDON_MPF_GROUPING, language: "Python", + tutorials: ["multi-product-formula"], }); } if (name === "qiskit-addon-sqd") { @@ -190,6 +209,10 @@ export class Pkg { githubSlug: "Qiskit/qiskit-addon-sqd", kebabCaseAndShortenUrls: true, language: "Python", + tutorials: [ + "sample-based-quantum-diagonalization", + "sample-based-krylov-quantum-diagonalization", + ], }); } if (name === "qiskit-addon-cutting") { @@ -199,6 +222,11 @@ export class Pkg { githubSlug: "Qiskit/qiskit-addon-cutting", kebabCaseAndShortenUrls: true, language: "Python", + tutorials: [ + "depth-reduction-with-circuit-cutting", + "periodic-boundary-conditions-with-circuit-cutting", + "wire-cutting", + ], }); } if (name === "qiskit-addon-utils") { @@ -210,6 +238,81 @@ export class Pkg { language: "Python", }); } + // if (name === "qiskit-addon-sqd-hpc") { + // return new Pkg({ + // ...args, + // title: "SQD for HPC", + // githubSlug: "Qiskit/qiskit-addon-sqd-hpc", + // kebabCaseAndShortenUrls: true, + // language: "C", + // }); + // } + if (name === "qiskit-addon-mthree") { + return new Pkg({ + ...args, + title: "Matrix-free Measurement Mitigation (M3)", + githubSlug: "Qiskit/qiskit-addon-mthree", + kebabCaseAndShortenUrls: true, + language: "Python", + releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), + }); + } + if (name === "qiskit-addon-pna") { + return new Pkg({ + ...args, + title: "Propagated noise absorption (PNA)", + githubSlug: "Qiskit/qiskit-addon-pna", + kebabCaseAndShortenUrls: true, + language: "Python", + }); + } + if (name === "qiskit-addon-slc") { + return new Pkg({ + ...args, + title: "Shaded lightcones", + githubSlug: "Qiskit/qiskit-addon-slc", + kebabCaseAndShortenUrls: true, + language: "Python", + }); + } + if (name === "qiskit-addon-opt-mapper") { + return new Pkg({ + ...args, + title: "Optimization mapper", + githubSlug: "Qiskit/qiskit-addon-opt-mapper", + kebabCaseAndShortenUrls: true, + language: "Python", + }); + } + // if (name === "qiskit-fermions") { + // return new Pkg({ + // ...args, + // title: "Fermionic mapper", + // githubSlug: "Qiskit/qiskit-fermions", + // kebabCaseAndShortenUrls: true, + // language: "Python", + // }); + // } + if (name === "qiskit-paulice") { + return new Pkg({ + ...args, + title: "Qiskit Paulice", + githubSlug: "Qiskit/qiskit-paulice", + kebabCaseAndShortenUrls: true, + language: "Python", + releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), + }); + } + if (name === "pauli-prop") { + return new Pkg({ + ...args, + title: "Pauli propagation", + githubSlug: "Qiskit/pauli-prop", + kebabCaseAndShortenUrls: true, + language: "Python", + releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), + }); + } if (name === "qiskit-c") { return new Pkg({ @@ -255,6 +358,10 @@ export class Pkg { } outputDir(parentDir: string): string { + return join(parentDir, this.name); + } + + apiOutputDir(parentDir: string): string { let path = join(parentDir, "api", this.name); if (this.isHistorical()) { path = join(path, this.versionWithoutPatch); @@ -284,6 +391,10 @@ export class Pkg { return this.language === "C"; } + isAddon(): boolean { + return Pkg.ADDON_NAMES.includes(this.name); + } + isProblematicLegacyQiskit(): boolean { return this.name === "qiskit" && +this.versionWithoutPatch < 0.45; } diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts b/scripts/js/lib/api/addonDocsPipeline.test.ts new file mode 100644 index 00000000000..a381054b624 --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts @@ -0,0 +1,110 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +import os from "os"; +import path from "path"; +import { mkdtemp, readFile, mkdir, copyFile } from "fs/promises"; + +import { globby } from "globby"; +import { expect, test } from "@playwright/test"; + +import { runAddonDocsPipeline } from "./addonDocsPipeline.js"; +import { Pkg, ReleaseNotesConfig } from "./Pkg.js"; + +// Snapshot test for the addon non-API docs pipeline. If output changes +// intentionally, run `npm test -- --updateSnapshot`. +// +// The fixture under `testdata/qiskit-addon-smoke/` covers the pipeline's key +// concerns: article extraction, relative-apidocs link rewriting, cross-package +// stub resolution via `ObjectsInv.loadPublishedApis`, notebook processing, and +// image routing to `public/docs/images/addons/{pkg}/`. + +const FIXTURE_DIR = "scripts/js/lib/api/testdata/qiskit-addon-smoke"; +const PUBLISHED_APIS_SEED = + "scripts/js/lib/api/testdata/qiskit-addon-smoke-publishedapis"; + +test("qiskit-addon-smoke addon docs pipeline", async ({}, testInfo) => { + testInfo.snapshotSuffix = ""; + + const tmpDir = await mkdtemp(path.join(os.tmpdir(), "addon-smoke-")); + const docsBaseFolder = path.join(tmpDir, "docs", "addons"); + const publicBaseFolder = path.join(tmpDir, "public", "docs"); + + // Seed public/docs/api//objects.inv so that loadPublishedApis() + // finds a sibling package's inventory for cross-package stub resolution. + const seededInvDir = path.join(publicBaseFolder, "api", "qiskit-addon-other"); + await mkdir(seededInvDir, { recursive: true }); + await copyFile( + path.join(PUBLISHED_APIS_SEED, "api", "qiskit-addon-other", "objects.inv"), + path.join(seededInvDir, "objects.inv"), + ); + + const pkg = new Pkg({ + name: "qiskit-addon-smoke", + title: "Qiskit Addon Smoke", + githubSlug: "Qiskit/qiskit-addon-smoke", + version: "0.0.1", + versionWithoutPatch: "0.0", + type: "latest", + language: "Python", + releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), + kebabCaseAndShortenUrls: true, + }); + + await runAddonDocsPipeline( + FIXTURE_DIR, + docsBaseFolder, + publicBaseFolder, + pkg, + ); + + const markdownFolder = pkg.outputDir(docsBaseFolder); + + // Invariant: the addon pipeline must never write API-shaped output under its + // own docs root. Guards against the class of bug that produced the stray + // `docs/addons/api/qiskit-addon-obp/` directory in the repo. + const strayUnderAddonsApi = await globby([`${docsBaseFolder}/api/**`]); + expect( + strayUnderAddonsApi, + "addon pipeline must not write to docs/addons/api/**", + ).toEqual([]); + + // Invariant: addon images must land under `public/docs/images/addons/{pkg}/`, + // not under `public/docs/images/api/{pkg}/`. The inverse was the bug before + // `saveImages` was parameterized. + const imagesUnderApi = await globby([`${publicBaseFolder}/images/api/**`]); + expect( + imagesUnderApi, + "addon-run images must not land under public/docs/images/api/**", + ).toEqual([]); + + const imagesUnderAddons = await globby([ + `${publicBaseFolder}/images/addons/**`, + ]); + expect( + imagesUnderAddons.length, + "addon-run images must land under public/docs/images/addons/**", + ).toBeGreaterThan(0); + + // --- Snapshot of every generated file under the addon output folder --- + + const resultFiles = await globby([`${markdownFolder}/**`]); + expect( + resultFiles.length, + "pipeline should generate at least one file", + ).toBeGreaterThan(0); + for (const file of resultFiles) { + const contents = await readFile(file, "utf-8"); + const fileName = path.parse(file).name; + expect(contents).toMatchSnapshot(fileName); + } +}); diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/-toc b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/-toc new file mode 100644 index 00000000000..ba1139127ae --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/-toc @@ -0,0 +1,55 @@ +{ + "parentUrl": "/docs/guides/addons", + "parentLabel": "Documentation", + "title": "Qiskit Addon Smoke 0.0.1", + "collapsed": true, + "children": [ + { + "title": "", + "children": [ + { + "title": "Home", + "url": "/docs/addons/qiskit-addon-smoke" + }, + { + "title": "Installation", + "url": "/docs/addons/qiskit-addon-smoke/install" + }, + { + "title": "Guides", + "children": [ + { + "title": "How to foo", + "url": "/docs/addons/qiskit-addon-smoke/how-tos/foo" + } + ] + }, + { + "title": "GitHub", + "url": "https://github.com/Qiskit/qiskit-addon-smoke" + } + ], + "collapsible": false + }, + { + "title": "Tutorials", + "children": [ + { + "title": "Intro tutorial", + "url": "/docs/addons/qiskit-addon-smoke/tutorials/intro" + } + ], + "collapsible": false + }, + { + "title": "API reference", + "collapsible": false, + "children": [ + { + "title": "Qiskit Addon Smoke API reference", + "url": "/docs/api/qiskit-addon-smoke" + } + ] + } + ] +} diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/foo b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/foo new file mode 100644 index 00000000000..1b578433808 --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/foo @@ -0,0 +1,13 @@ +--- +title: "How to foo" +description: "How to foo for the lastest version of Qiskit Addon Smoke" +--- + +# How to foo + +This how-to references the [API docs index](/docs/api/qiskit-addon-smoke/index) and the [do\_thing](/docs/api/qiskit-addon-smoke/smoke-do-thing) symbol from the same package. + +It also links into another package via [other\_thing](/docs/api/qiskit-addon-other/other_thing). + +![A reused smoke diagram](/docs/images/addons/qiskit-addon-smoke/smoke-diagram.avif) + diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/index b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/index new file mode 100644 index 00000000000..a7a464e0786 --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/index @@ -0,0 +1,11 @@ +--- +title: "Qiskit Addon Smoke" +description: "Documentation for the latest version of Qiskit Addon Smoke" +--- + +# Qiskit Addon Smoke + +This is the smoke-test landing page. See the [installation instructions](install) or the [first how-to](how-tos/foo). + +![A smoke test diagram](/docs/images/addons/qiskit-addon-smoke/smoke-diagram.avif) + diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/install b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/install new file mode 100644 index 00000000000..0444ef6d17e --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/install @@ -0,0 +1,9 @@ +--- +title: "Installation" +description: "Installation for the lastest version of Qiskit Addon Smoke" +--- + +# Installation + +Install with `pip`. Back to the [home page](index). + diff --git a/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/intro b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/intro new file mode 100644 index 00000000000..a2c40ee0d38 --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.test.ts-snapshots/intro @@ -0,0 +1,37 @@ +{ + "cells": [ + { + "id": "frontmatter", + "cell_type": "markdown", + "source": "---\ntitle: \"Intro tutorial\"\ndescription: \"Intro tutorial for the latest version of Qiskit Addon Smoke\"\n---", + "metadata": {} + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Intro tutorial\n", + "\n", + "This notebook references [do_thing](/docs/api/qiskit-addon-smoke/smoke-do-thing#smoke.do_thing) from the current package and [other_thing](/docs/api/qiskit-addon-other/other_thing) from a sibling package.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('hello from the smoke tutorial')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/scripts/js/lib/api/addonDocsPipeline.ts b/scripts/js/lib/api/addonDocsPipeline.ts new file mode 100644 index 00000000000..3a3e0dd1be4 --- /dev/null +++ b/scripts/js/lib/api/addonDocsPipeline.ts @@ -0,0 +1,170 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2024. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Pipeline for generating addon content pages from a Sphinx build artifact. +// Addon docs differ from API docs in several ways: +// - Content lives under docs/addons/{pkg}/ rather than docs/api/{pkg}/. +// - The Sphinx artifact contains how-to guides and explanations (HTML), +// Jupyter notebooks (.ipynb), and static images — but NOT apidoc stubs. +// - A _toc.json is generated by generateAddonToc (not generateToc) because +// the TOC shape for addons includes a back-link to the parent page and +// optional Tutorials and API reference sections. +// - When a notebook and an HTML file share the same base name, the notebook +// wins so we preserve code outputs rather than the rendered HTML version. + +import { writeFile } from "fs/promises"; + +import { globby } from "globby"; +import { mkdirp } from "mkdirp"; + +import { ObjectsInv } from "./objectsInv.js"; +import { Pkg } from "./Pkg.js"; +import { + convertHtmlToMarkdown, + copyImages, + postProcess, + writeMarkdownResults, +} from "./pipelineStages.js"; +import { + collectNotebookImages, + processNotebooks, + readNotebooks, + writeNotebooks, +} from "./notebookStages.js"; +import { DOCS_BASE_PATH } from "./paths.js"; +import { generateAddonToc } from "./generateAddonToc.js"; + +// Sphinx build artifacts that are never content. +const SPHINX_INTERNALS = [ + "_static/**", + "_sources/**", + "_downloads/**", + "_modules/**", + "genindex.html", + "py-modindex.html", + "search.html", + "objects.inv", +]; + +/** + * Run the full addon docs pipeline for one package version. + * + * @param artifactPath Root of the Sphinx HTML build (the unzipped artifact folder). + * @param docsBaseFolder Repo-relative output root for markdown, e.g. "docs/addons". + * @param publicBaseFolder Repo-relative output root for public assets, e.g. "public/docs". + * @param pkg Package metadata (name, version, tutorial slugs, etc.). + */ +export async function runAddonDocsPipeline( + artifactPath: string, + docsBaseFolder: string, + publicBaseFolder: string, + pkg: Pkg, +): Promise { + const allObjectInvs = await ObjectsInv.loadPublishedApis(publicBaseFolder); + const [files, outputPath, objectsInv] = await determineFilePaths( + artifactPath, + docsBaseFolder, + pkg, + ); + + // HTML → Markdown. The last argument (true) enables extracting frontmatter + // from the Sphinx HTML

rather than letting addFrontMatter.ts generate it. + const htmlFiles = files.filter((f) => f.endsWith(".html")); + const initialResults = await convertHtmlToMarkdown( + pkg, + artifactPath, + docsBaseFolder, + outputPath, + htmlFiles, + pkg.outputDir(`${DOCS_BASE_PATH}/images/addons`), + true, + ); + + const results = await postProcess( + pkg, + initialResults, + objectsInv, + allObjectInvs, + ); + await writeMarkdownResults(pkg, docsBaseFolder, results); + + // Notebooks. Images embedded in notebooks are collected separately so they + // can be passed to copyImages together with the HTML-derived images. + const notebookFiles = files.filter((f) => f.endsWith(".ipynb")); + const initialNotebooks = await readNotebooks( + artifactPath, + docsBaseFolder, + outputPath, + notebookFiles, + ); + const imageDestination = pkg.outputDir(`${DOCS_BASE_PATH}/images/addons`); + const notebookImages = collectNotebookImages(initialNotebooks, imageDestination); + const notebooks = processNotebooks( + initialNotebooks, + objectsInv, + allObjectInvs, + pkg, + imageDestination, + ); + await writeNotebooks(pkg, docsBaseFolder, notebooks); + + // Copy all images (HTML-referenced + notebook-referenced) into public/. + await copyImages( + pkg, + artifactPath, + pkg.outputDir(`${publicBaseFolder}/images/addons`), + results, + notebookImages, + ); + + console.log("Generating addon toc"); + const toc = await generateAddonToc(pkg, docsBaseFolder, artifactPath); + await writeFile( + `${outputPath}/_toc.json`, + JSON.stringify(toc, null, 2) + "\n", + ); +} + +async function determineFilePaths( + htmlPath: string, + docsBaseFolder: string, + pkg: Pkg, +): Promise<[string[], string, ObjectsInv]> { + const objectsInv = await ObjectsInv.fromFile(htmlPath, pkg.language); + + const allFiles = await globby(["**"], { + cwd: htmlPath, + ignore: [ + "apidocs/**", + "apidoc/**", + "stubs/**", + "tutorials/**", + "release-notes.html", + "release_notes.html", + ...SPHINX_INTERNALS, + ], + }); + + // Prefer .ipynb over .html when both exist for the same base path. + const notebookBases = new Set( + allFiles + .filter((f) => f.endsWith(".ipynb")) + .map((f) => f.slice(0, -".ipynb".length)), + ); + const files = allFiles.filter( + (f) => + !f.endsWith(".html") || !notebookBases.has(f.slice(0, -".html".length)), + ); + const outputPath = pkg.outputDir(docsBaseFolder); + await mkdirp(outputPath); + return [files, outputPath, objectsInv]; +} diff --git a/scripts/js/lib/api/conversionPipeline.test.ts b/scripts/js/lib/api/apiDocsPipeline.test.ts similarity index 95% rename from scripts/js/lib/api/conversionPipeline.test.ts rename to scripts/js/lib/api/apiDocsPipeline.test.ts index 90d1c91bd21..3fc28c73ce8 100644 --- a/scripts/js/lib/api/conversionPipeline.test.ts +++ b/scripts/js/lib/api/apiDocsPipeline.test.ts @@ -17,7 +17,7 @@ import { mkdtemp, readFile } from "fs/promises"; import { globby } from "globby"; import { expect, test } from "@playwright/test"; -import { runConversionPipeline } from "./conversionPipeline.js"; +import { runApiDocsPipeline } from "./apiDocsPipeline.js"; import { Pkg, ReleaseNotesConfig } from "./Pkg.js"; // This test uses snapshot testing (https://jestjs.io/docs/snapshot-testing#updating-snapshots). If the tests fail and the changes @@ -66,9 +66,9 @@ test("qiskit-sphinx-theme", async ({}, testInfo) => { releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), kebabCaseAndShortenUrls: false, }); - const markdownFolder = pkg.outputDir(docsBaseFolder); + const markdownFolder = pkg.apiOutputDir(docsBaseFolder); - await runConversionPipeline( + await runApiDocsPipeline( "scripts/js/lib/api/testdata/qiskit-sphinx-theme", docsBaseFolder, publicBaseFolder, diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/-package b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/-package similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/-package rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/-package diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/-toc b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/-toc similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/-toc rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/-toc diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/api-example.Electron b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/api-example.Electron similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/api-example.Electron rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/api-example.Electron diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/api-example.my_function1 b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/api-example.my_function1 similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/api-example.my_function1 rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/api-example.my_function1 diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/index b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/index similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/index rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/index diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/inline-classes b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/inline-classes similarity index 100% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/inline-classes rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/inline-classes diff --git a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/module b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/module similarity index 59% rename from scripts/js/lib/api/conversionPipeline.test.ts-snapshots/module rename to scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/module index c98fe8a279c..aaf48736d3d 100644 --- a/scripts/js/lib/api/conversionPipeline.test.ts-snapshots/module +++ b/scripts/js/lib/api/apiDocsPipeline.test.ts-snapshots/module @@ -20,14 +20,14 @@ Welcome to my super cool module! This is an example! -Testing internal references… [`Electron.compute_momentum()`](api_example.Electron#compute_momentum "api_example.Electron.compute_momentum"). +Testing internal references… [`Electron.compute_momentum()`](/docs/api/qiskit-sphinx-theme/api_example.Electron#compute_momentum "api_example.Electron.compute_momentum"). ## Contents -| | | -| ------------------------------------------------------------------------------------------------ | ----------------------------------- | -| [`Electron`](api_example.Electron "api_example.Electron")(\[size, name]) | A representation of an electron. | -| [`my_function1`](api_example.my_function1 "api_example.my_function1")(input1, input2\[, input3]) | A function that does awesome stuff. | +| | | +| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------- | +| [`Electron`](/docs/api/qiskit-sphinx-theme/api_example.Electron "api_example.Electron")(\[size, name]) | A representation of an electron. | +| [`my_function1`](/docs/api/qiskit-sphinx-theme/api_example.my_function1 "api_example.my_function1")(input1, input2\[, input3]) | A function that does awesome stuff. | ## Functions diff --git a/scripts/js/lib/api/apiDocsPipeline.ts b/scripts/js/lib/api/apiDocsPipeline.ts new file mode 100644 index 00000000000..bdc4258b088 --- /dev/null +++ b/scripts/js/lib/api/apiDocsPipeline.ts @@ -0,0 +1,121 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2024. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +import { writeFile } from "fs/promises"; + +import { mkdirp } from "mkdirp"; +import { globby } from "globby"; + +import { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; +import { ObjectsInv } from "./objectsInv.js"; +import { Pkg } from "./Pkg.js"; +import { generateToc } from "./generateToc.js"; +import { maybeUpdateReleaseNotesFolder } from "./releaseNotes.js"; +import { + convertHtmlToMarkdown, + copyImages, + postProcess, + writeMarkdownResults, +} from "./pipelineStages.js"; +import { C_API_BASE_PATH, DOCS_BASE_PATH } from "./paths.js"; + +export async function runApiDocsPipeline( + artifactPath: string, + docsBaseFolder: string, + publicBaseFolder: string, + pkg: Pkg, +) { + const allObjectInvs = await ObjectsInv.loadPublishedApis(publicBaseFolder); + const [files, markdownPath, maybeObjectsInv] = await determineFilePaths( + artifactPath, + docsBaseFolder, + pkg, + ); + + const initialResults = await convertHtmlToMarkdown( + pkg, + artifactPath, + docsBaseFolder, + markdownPath, + files, + pkg.apiOutputDir(`${DOCS_BASE_PATH}/images`), + ); + + const results = await postProcess( + pkg, + initialResults, + maybeObjectsInv, + allObjectInvs, + ); + + // Warning: the sequence of operations often matters. + await writeMarkdownResults(pkg, docsBaseFolder, results); + // `publicBaseFolder` is passed as "public/docs"; images go under + // "public/docs/images/api/{pkg}". See also maybeObjectsInv.write below which + // uses pkg.apiOutputDir(publicBaseFolder) — the two outputs share the same + // `api/{pkg}` subtree inside publicBaseFolder. + await copyImages( + pkg, + artifactPath, + pkg.apiOutputDir(`${publicBaseFolder}/images`), + results, + ); + await maybeObjectsInv?.write(pkg.apiOutputDir(publicBaseFolder)); + await maybeUpdateReleaseNotesFolder(pkg, markdownPath); + await writeTocFile(pkg, markdownPath, results); + await writeVersionFile(pkg, markdownPath); +} + +async function determineFilePaths( + htmlPath: string, + docsBaseFolder: string, + pkg: Pkg, +): Promise<[string[], string, ObjectsInv | undefined]> { + const maybeObjectsInv = await (pkg.isProblematicLegacyQiskit() + ? undefined + : ObjectsInv.fromFile(htmlPath, pkg.language)); + + const extraFiles = pkg.isCApi() + ? [`${C_API_BASE_PATH}/**.html`, "apidocs/**.html"] + : ["apidocs/**.html", "apidoc/**.html", "stubs/**.html"]; + const files = await globby( + [...extraFiles, "release_notes.html", "release-notes.html"], + { + cwd: htmlPath, + }, + ); + const markdownPath = pkg.apiOutputDir(docsBaseFolder); + await mkdirp(markdownPath); + return [files, markdownPath, maybeObjectsInv]; +} + +async function writeTocFile( + pkg: Pkg, + markdownPath: string, + results: HtmlToMdResultWithUrl[], +): Promise { + console.log("Generating toc"); + const toc = generateToc(pkg, results); + await writeFile( + `${markdownPath}/_toc.json`, + JSON.stringify(toc, null, 2) + "\n", + ); +} + +async function writeVersionFile(pkg: Pkg, markdownPath: string): Promise { + console.log("Generating version file"); + const pkg_json = { name: pkg.name, version: pkg.version }; + await writeFile( + `${markdownPath}/_package.json`, + JSON.stringify(pkg_json, null, 2) + "\n", + ); +} diff --git a/scripts/js/lib/api/conversionPipeline.ts b/scripts/js/lib/api/conversionPipeline.ts deleted file mode 100644 index d672da35b17..00000000000 --- a/scripts/js/lib/api/conversionPipeline.ts +++ /dev/null @@ -1,212 +0,0 @@ -// This code is a Qiskit project. -// -// (C) Copyright IBM 2024. -// -// This code is licensed under the Apache License, Version 2.0. You may -// obtain a copy of this license in the LICENSE file in the root directory -// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -// -// Any modifications or derivative works of this code must retain this -// copyright notice, and modified files need to carry a notice indicating -// that they have been altered from the originals. - -import { join, parse, relative } from "path"; -import { readFile, writeFile } from "fs/promises"; - -import { mkdirp } from "mkdirp"; -import { globby } from "globby"; -import { uniqBy } from "lodash-es"; - -import { ObjectsInv } from "./objectsInv.js"; -import { sphinxHtmlToMarkdown } from "./htmlToMd.js"; -import { saveImages } from "./saveImages.js"; -import { generateToc } from "./generateToc.js"; -import { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; -import { mergeClassMembers } from "./mergeClassMembers.js"; -import { normalizeResultUrls } from "./normalizeResultUrls.js"; -import { updateLinks } from "./updateLinks.js"; -import { specialCaseResults } from "./specialCaseResults.js"; -import addFrontMatter from "./addFrontMatter.js"; -import { dedupeHtmlIdsFromResults } from "./dedupeHtmlIds.js"; -import removeMathBlocksIndentation from "./removeMathBlocksIndentation.js"; -import { Pkg } from "./Pkg.js"; -import { - maybeUpdateReleaseNotesFolder, - handleReleaseNotesFile, -} from "./releaseNotes.js"; - -// This is the folder that contains all C API docs in the Sphinx artifact. -export const C_API_BASE_PATH = "cdoc" as const; - -export const DOCS_BASE_PATH = "/docs"; - -export async function runConversionPipeline( - htmlPath: string, - docsBaseFolder: string, - publicBaseFolder: string, - pkg: Pkg, -) { - const [files, markdownPath, maybeObjectsInv] = await determineFilePaths( - htmlPath, - docsBaseFolder, - pkg, - ); - let initialResults = await convertFilesToMarkdown( - pkg, - htmlPath, - docsBaseFolder, - markdownPath, - files, - ); - - const results = await postProcessResults( - pkg, - maybeObjectsInv, - initialResults, - ); - - // Warning: the sequence of operations often matters. - await writeMarkdownResults(pkg, docsBaseFolder, results); - await copyImages(pkg, htmlPath, "public", results); - await maybeObjectsInv?.write(pkg.outputDir(publicBaseFolder)); - await maybeUpdateReleaseNotesFolder(pkg, markdownPath); - await writeTocFile(pkg, markdownPath, results); - await writeVersionFile(pkg, markdownPath); -} - -async function determineFilePaths( - htmlPath: string, - docsBaseFolder: string, - pkg: Pkg, -): Promise<[string[], string, ObjectsInv | undefined]> { - const maybeObjectsInv = await (pkg.isProblematicLegacyQiskit() - ? undefined - : ObjectsInv.fromFile(htmlPath, pkg.language)); - - const extraFiles = pkg.isCApi() - ? [`${C_API_BASE_PATH}/**.html`] - : ["apidocs/**.html", "apidoc/**.html", "stubs/**.html"]; - const files = await globby( - [...extraFiles, "release_notes.html", "release-notes.html"], - { - cwd: htmlPath, - }, - ); - const markdownPath = pkg.outputDir(docsBaseFolder); - await mkdirp(markdownPath); - return [files, markdownPath, maybeObjectsInv]; -} - -async function convertFilesToMarkdown( - pkg: Pkg, - htmlPath: string, - docsBaseFolder: string, - markdownPath: string, - filePaths: string[], -): Promise { - const results = []; - for (const file of filePaths) { - const html = await readFile(join(htmlPath, file), "utf-8"); - const result = await sphinxHtmlToMarkdown({ - html, - fileName: file, - determineGithubUrl: pkg.determineGithubUrlFn(), - imageDestination: pkg.outputDir(`${DOCS_BASE_PATH}/images`), - releaseNotesTitle: pkg.releaseNotesTitle(), - hasSeparateReleaseNotes: pkg.hasSeparateReleaseNotes(), - isCApi: pkg.isCApi(), - hasRootNamespaceFile: pkg.hasRootNamespaceFile, - }); - - // Avoid creating an empty markdown file for HTML files without content - // (e.g. HTML redirects) - if (result.markdown == "") { - continue; - } - - const { dir, name } = parse(`${markdownPath}/${file}`); - let url = `/${relative(docsBaseFolder, dir)}/${name}`; - results.push({ ...result, url }); - } - return results; -} - -async function copyImages( - pkg: Pkg, - htmlPath: string, - publicBaseFolder: string, - results: HtmlToMdResultWithUrl[], -): Promise { - console.log("Saving images"); - const allImages = uniqBy( - results.flatMap((result) => result.images), - (image) => image.fileName, - ); - await saveImages(allImages, `${htmlPath}/_images`, publicBaseFolder, pkg); -} - -async function postProcessResults( - pkg: Pkg, - maybeObjectsInv: ObjectsInv | undefined, - initialResults: HtmlToMdResultWithUrl[], -): Promise { - const results = await mergeClassMembers(initialResults); - normalizeResultUrls(results, { - kebabCaseAndShorten: pkg.kebabCaseAndShortenUrls, - pkgName: pkg.name, - }); - specialCaseResults(results); - await updateLinks( - results, - { - kebabCaseAndShorten: pkg.kebabCaseAndShortenUrls, - pkgName: pkg.name, - pkgOutputDir: pkg.outputDir(DOCS_BASE_PATH), - }, - maybeObjectsInv, - ); - await dedupeHtmlIdsFromResults(results); - addFrontMatter(results, pkg); - removeMathBlocksIndentation(results); - return results; -} - -async function writeMarkdownResults( - pkg: Pkg, - docsBaseFolder: string, - results: HtmlToMdResultWithUrl[], -): Promise { - for (const result of results) { - let path = `${docsBaseFolder}${result.url}.mdx`; - if (path.endsWith("release-notes.mdx")) { - if (!pkg.releaseNotesConfig.enabled) continue; - - const shouldWriteResult = await handleReleaseNotesFile(result, pkg); - if (!shouldWriteResult) continue; - } - - await writeFile(path, result.markdown); - } -} - -async function writeTocFile( - pkg: Pkg, - markdownPath: string, - results: HtmlToMdResultWithUrl[], -): Promise { - console.log("Generating toc"); - const toc = generateToc(pkg, results); - await writeFile( - `${markdownPath}/_toc.json`, - JSON.stringify(toc, null, 2) + "\n", - ); -} - -async function writeVersionFile(pkg: Pkg, markdownPath: string): Promise { - console.log("Generating version file"); - const pkg_json = { name: pkg.name, version: pkg.version }; - await writeFile( - `${markdownPath}/_package.json`, - JSON.stringify(pkg_json, null, 2) + "\n", - ); -} diff --git a/scripts/js/lib/api/generateAddonToc.test.ts b/scripts/js/lib/api/generateAddonToc.test.ts new file mode 100644 index 00000000000..1a7b2a54450 --- /dev/null +++ b/scripts/js/lib/api/generateAddonToc.test.ts @@ -0,0 +1,311 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +import os from "os"; +import path from "path"; +import { mkdtemp, mkdir, writeFile } from "fs/promises"; + +import { expect, test } from "@playwright/test"; + +import { generateAddonToc } from "./generateAddonToc.js"; +import { Pkg, ReleaseNotesConfig } from "./Pkg.js"; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +async function makePkg(name = "my-addon", githubSlug?: string): Promise { + return new Pkg({ + name, + title: "My Addon", + githubSlug, + version: "1.2.0", + versionWithoutPatch: "1.2", + type: "latest", + language: "Python", + releaseNotesConfig: new ReleaseNotesConfig({ enabled: false }), + kebabCaseAndShortenUrls: true, + }); +} + +/** + * Builds a minimal Sphinx-style index.html with a sidebar tree. + * Each item in `navItems` can be: + * - a string href (top-level page link) + * - { href, title } (top-level page with custom title) + * - { href, title, children: [{ href, title }] } (section with sub-pages) + */ +type NavChild = { href: string; title: string }; +type NavItem = + | string + | { href: string; title?: string; children?: NavChild[] }; + +function makeIndexHtml(navItems: NavItem[]): string { + const items = navItems.map((item) => { + if (typeof item === "string") { + const title = item.replace(/\.html$/, "").replace(/[-_]/g, " "); + return `
  • ${title}
  • `; + } + const { href, title = href.replace(/\.html$/, ""), children } = item; + if (children && children.length > 0) { + const l2s = children + .map( + (c) => + `
  • ${c.title}
  • `, + ) + .join("\n "); + return `
  • ${title} +
      ${l2s}
    +
  • `; + } + return `
  • ${title}
  • `; + }); + + return ` + + + + +`; +} + +/** Creates a temp directory with an artifact (index.html) and optional docs files. */ +async function makeTestDirs( + navItems: NavItem[], +): Promise<{ artifactDir: string; docsDir: string }> { + const tmpDir = await mkdtemp(path.join(os.tmpdir(), "addon-toc-test-")); + const artifactDir = path.join(tmpDir, "artifact"); + const docsDir = path.join(tmpDir, "addons"); + await mkdir(artifactDir, { recursive: true }); + await mkdir(path.join(docsDir, "my-addon"), { recursive: true }); + await writeFile( + path.join(artifactDir, "index.html"), + makeIndexHtml(navItems), + "utf-8", + ); + return { artifactDir, docsDir }; +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +test("minimal addon: only index and install in sidebar", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "My Addon" }, + { href: "install.html", title: "Installation" }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + + expect(toc).toEqual({ + parentUrl: "/docs/guides/addons", + parentLabel: "Documentation", + title: "My Addon 1.2.0", + collapsed: true, + children: [ + { + title: "", + collapsible: false, + children: [ + { title: "My Addon", url: "/docs/addons/my-addon" }, + { title: "Installation", url: "/docs/addons/my-addon/install" }, + ], + }, + { + title: "API reference", + collapsible: false, + children: [ + { + title: "My Addon API reference", + url: "/docs/api/my-addon", + }, + ], + }, + ], + }); +}); + +test("sidebar order is preserved exactly", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + { href: "install.html", title: "Install" }, + { href: "changelog.html", title: "Changelog" }, + { href: "faq.html", title: "FAQ" }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + expect(main.children?.map((c) => c.title)).toEqual([ + "Home", + "Install", + "Changelog", + "FAQ", + ]); +}); + +test("subdirectory section preserves l2 children order from sidebar", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + { + href: "how_tos/index.html", + title: "Guides", + children: [ + { href: "how_tos/beta.html", title: "Beta guide" }, + { href: "how_tos/alpha.html", title: "Alpha guide" }, + { href: "how_tos/gamma.html", title: "Gamma guide" }, + ], + }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + const guides = main.children?.find((c) => c.title === "Guides"); + expect(guides?.children?.map((c) => c.title)).toEqual([ + "Beta guide", + "Alpha guide", + "Gamma guide", + ]); +}); + +test("subdirectory section has correct child URLs", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + { + href: "how_tos/index.html", + title: "Guides", + children: [ + { href: "how_tos/my_guide.html", title: "My Guide" }, + ], + }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + const guides = main.children?.find((c) => c.title === "Guides"); + expect(guides?.children?.[0].url).toBe( + "/docs/addons/my-addon/how_tos/my-guide", + ); +}); + +test("items with titles matching SKIP_ITEMS are omitted", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + { + href: "tutorials/index.html", + title: "Tutorials", + children: [{ href: "tutorials/demo.html", title: "Demo" }], + }, + { href: "install.html", title: "Install" }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + expect(main.children?.map((c) => c.title)).toEqual(["Home", "Install"]); +}); + +test("external link (class=external) is passed through unchanged", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + ]); + // Append an external link directly to the index.html sidebar + const indexPath = path.join(artifactDir, "index.html"); + const html = await import("fs/promises").then((fs) => + fs.readFile(indexPath, "utf-8"), + ); + await import("fs/promises").then((fs) => + fs.writeFile( + indexPath, + html.replace( + "", + `
  • GitHub
  • `, + ), + ), + ); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + expect(main.children?.at(-1)).toEqual({ + title: "GitHub", + url: "https://github.com/Qiskit/my-addon", + }); +}); + +test("href='#' maps to package root URL", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + expect(main.children?.[0].url).toBe("/docs/addons/my-addon"); +}); + +test("api reference section is always last", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const last = toc.children.at(-1); + + expect(last?.title).toBe("API reference"); + expect(last?.children?.[0].url).toBe("/docs/api/my-addon"); +}); + +test("missing index.html throws", async () => { + const tmpDir = await mkdtemp(path.join(os.tmpdir(), "addon-toc-test-")); + const artifactDir = path.join(tmpDir, "artifact"); + const docsDir = path.join(tmpDir, "addons"); + await mkdir(artifactDir, { recursive: true }); + await mkdir(path.join(docsDir, "my-addon"), { recursive: true }); + + const pkg = await makePkg(); + await expect(generateAddonToc(pkg, docsDir, artifactDir)).rejects.toThrow(); +}); + +test("subdirectory section has no url property on the section entry", async () => { + const { artifactDir, docsDir } = await makeTestDirs([ + { href: "#", title: "Home" }, + { + href: "how_tos/index.html", + title: "Guides", + children: [{ href: "how_tos/guide.html", title: "A guide" }], + }, + ]); + + const pkg = await makePkg(); + const toc = await generateAddonToc(pkg, docsDir, artifactDir); + const main = toc.children[0]; + + const guidesEntry = main.children?.find((c) => c.title === "Guides"); + expect(guidesEntry?.url).toBeUndefined(); + expect(guidesEntry?.children).toHaveLength(1); +}); diff --git a/scripts/js/lib/api/generateAddonToc.ts b/scripts/js/lib/api/generateAddonToc.ts new file mode 100644 index 00000000000..7eec021b08d --- /dev/null +++ b/scripts/js/lib/api/generateAddonToc.ts @@ -0,0 +1,178 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Generates the _toc.json for an addon's content pages. +// +// Addon TOCs have a fixed three-part shape: +// 1. A "main" section built from the Sphinx sidebar tree in index.html — +// preserving the author's toctree order and titles exactly. +// 2. An optional "Tutorials" section listing slugs from docs/tutorials/. +// 3. An "API reference" section linking to docs/api/{pkg}. +// +// Called by addonDocsPipeline.ts; for API doc TOCs see generateToc.ts. + +import { readFile } from "fs/promises"; +import { join, parse } from "path/posix"; + +import { load } from "cheerio"; + +import { Pkg } from "./Pkg.js"; +import { TocEntry } from "./generateToc.js"; +import { DOCS_BASE_PATH } from "./paths.js"; +import { kebabCaseAndShortenPage } from "./normalizeResultUrls.js"; + +type AddonTocSection = TocEntry & { collapsible?: boolean }; + +type AddonToc = { + parentUrl: string; + parentLabel: string; + title: string; + collapsed: true; + children: AddonTocSection[]; +}; + +export async function generateAddonToc( + pkg: Pkg, + docsBaseFolder: string, + artifactPath: string, +): Promise { + const addonUrlBase = `${DOCS_BASE_PATH}/addons/${pkg.name}`; + + const mainChildren = await buildMainFromSidebar( + artifactPath, + pkg, + addonUrlBase, + ); + + const topLevelSections: AddonTocSection[] = []; + + if (pkg.tutorials.length > 0) { + const tutorialsToc = JSON.parse( + await readFile( + join(docsBaseFolder, "..", "tutorials", "_toc.json"), + "utf-8", + ), + ); + const tutorialChildren: TocEntry[] = pkg.tutorials.map((slug) => ({ + title: findTutorialTitle(tutorialsToc, slug) ?? slug, + url: `${DOCS_BASE_PATH}/tutorials/${slug}`, + })); + topLevelSections.push({ + title: "Tutorials", + collapsible: false, + children: tutorialChildren, + }); + } + + topLevelSections.push({ + title: "API reference", + collapsible: false, + children: [ + { + title: `${pkg.title} API reference`, + url: `${DOCS_BASE_PATH}/api/${pkg.name}`, + }, + ], + }); + + return { + parentUrl: "/docs/guides/addons", + parentLabel: "Documentation", + title: `${pkg.title} ${pkg.version}`, + collapsed: true, + children: [ + { title: "", children: mainChildren, collapsible: false }, + ...topLevelSections, + ], + }; +} + +const SKIP_ITEMS = ["api reference", "release notes", "tutorials"]; + +/** + * Builds the main TOC section from the sidebar tree in the Sphinx index.html. + * Each toctree-l1 item becomes either a flat entry (top-level page) or a + * section with children (subdirectory). Titles come directly from the sidebar + * link text — exactly what the docs author wrote in their toctree. + */ +async function buildMainFromSidebar( + artifactPath: string, + pkg: Pkg, + addonUrlBase: string, +): Promise { + const html = await readFile(join(artifactPath, "index.html"), "utf-8"); + const $ = load(html); + const entries: TocEntry[] = []; + + $(".sidebar-tree > ul > li").each((_, l1) => { + const $l1 = $(l1); + const a = $l1.children("a").first(); + const href = a.attr("href") ?? ""; + const title = a.text().trim(); + + if (SKIP_ITEMS.includes(title.toLowerCase())) return; + + // External link (e.g. GitHub) + if (a.hasClass("external")) { + entries.push({ title, url: href }); + return; + } + + // href="#" means "this page" (index.html) in Sphinx when current-page is active + if (href === "#") { + entries.push({ title, url: addonUrlBase }); + return; + } + + // Subdirectory section: has-children class with l2 items + if ($l1.hasClass("has-children")) { + const children: TocEntry[] = []; + $l1.find("ul > li > a").each((_, a2) => { + const childHref = $(a2).attr("href") ?? ""; + const dir = childHref.split("/")[0]; + const slug = hrefToSlug(childHref, pkg); + children.push({ + title: $(a2).text().trim(), + url: `${addonUrlBase}/${dir}/${slug}`, + }); + }); + if (children.length > 0) entries.push({ title, children }); + return; + } + + const slug = hrefToSlug(href, pkg); + // Top-level page (may be a subdirectory index like "explanation/index.html") + const path = href.includes("/") ? `${href.split("/")[0]}/${slug}` : slug; + entries.push({ title, url: `${addonUrlBase}/${path}` }); + }); + + return entries; +} + +/** Converts a Sphinx HTML href filename to the kebab-case slug used in the output MDX. */ +function hrefToSlug(href: string, pkg: Pkg): string { + const name = parse(href).name; + return pkg.kebabCaseAndShortenUrls + ? kebabCaseAndShortenPage(name, pkg.name) + : name; +} + +type TocNode = { title?: string; url?: string; children?: TocNode[] }; + +/** Recursively searches a TOC tree for a node whose URL ends with the given slug. */ +function findTutorialTitle(node: TocNode, slug: string): string | undefined { + if (node.url?.endsWith(`/${slug}`) && node.title) return node.title; + for (const child of node.children ?? []) { + const found = findTutorialTitle(child, slug); + if (found) return found; + } +} diff --git a/scripts/js/lib/api/generateToc.ts b/scripts/js/lib/api/generateToc.ts index 2001244c4b9..f89c5255e40 100644 --- a/scripts/js/lib/api/generateToc.ts +++ b/scripts/js/lib/api/generateToc.ts @@ -16,7 +16,7 @@ import { getLastPartFromFullIdentifier } from "../stringUtils.js"; import { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; import { Pkg } from "./Pkg.js"; import type { TocGrouping } from "./TocGrouping.js"; -import { DOCS_BASE_PATH } from "./conversionPipeline.js"; +import { DOCS_BASE_PATH } from "./paths.js"; import { groupByMajorVersion } from "./releaseNotes.js"; export type TocEntry = { @@ -35,6 +35,8 @@ type Toc = { children: TocEntry[]; collapsed: boolean; untranslatable?: boolean; + parentUrl?: string; + parentLabel?: string; }; export function generateToc(pkg: Pkg, results: HtmlToMdResultWithUrl[]): Toc { @@ -71,6 +73,10 @@ export function generateToc(pkg: Pkg, results: HtmlToMdResultWithUrl[]): Toc { children: orderedEntries, collapsed: true, untranslatable: true, + ...(pkg.isAddon() && { + parentUrl: `/docs/addons/${pkg.name}`, + parentLabel: pkg.title, + }), }; } @@ -226,7 +232,7 @@ function ensureIndexPage( pkg: Pkg, tocModules: TocEntry[], ): TocEntry | undefined { - const docsFolder = pkg.outputDir(`${DOCS_BASE_PATH}/`); + const docsFolder = pkg.apiOutputDir(`${DOCS_BASE_PATH}/`); return tocModules.some((entry) => entry.url === docsFolder) ? undefined : { diff --git a/scripts/js/lib/api/htmlToMd.ts b/scripts/js/lib/api/htmlToMd.ts index 7abf0b466bf..9d4d5a43bdb 100644 --- a/scripts/js/lib/api/htmlToMd.ts +++ b/scripts/js/lib/api/htmlToMd.ts @@ -243,9 +243,10 @@ function buildAdmonition( handlers: Record, ): MdxJsxFlowElement { const titleNode = findNodeWithProperty(node.children, "admonition-title"); - const children: Array = without(node.children, titleNode).map( - (node: any) => toMdast(node, { handlers }), - ); + const children: Array = without( + node.children, + titleNode ?? undefined, + ).map((node: any) => toMdast(node, { handlers })); let type = "note"; if (nodeClasses.includes("warning")) { diff --git a/scripts/js/lib/api/normalizeResultUrls.ts b/scripts/js/lib/api/normalizeResultUrls.ts index 375411fe6f1..f50679b8d09 100644 --- a/scripts/js/lib/api/normalizeResultUrls.ts +++ b/scripts/js/lib/api/normalizeResultUrls.ts @@ -13,7 +13,7 @@ import { kebabCase, initial, last } from "lodash-es"; import { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; -import { C_API_BASE_PATH } from "./conversionPipeline.js"; +import { C_API_BASE_PATH } from "./paths.js"; import { removePart } from "../stringUtils.js"; export function kebabCaseAndShortenPage(page: string, pkgName: string): string { diff --git a/scripts/js/lib/api/notebookStages.ts b/scripts/js/lib/api/notebookStages.ts new file mode 100644 index 00000000000..6e2ecd73d69 --- /dev/null +++ b/scripts/js/lib/api/notebookStages.ts @@ -0,0 +1,210 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Jupyter notebook stages used by the addon docs pipeline. The API pipeline +// does not process notebooks today, but these stages live alongside the other +// shared stages so that any future pipeline needing notebook handling can +// reuse them without duplication. + +import { dirname, parse, relative } from "path"; +import { readFile, writeFile } from "fs/promises"; + +import { mkdirp } from "mkdirp"; +import { visit, EXIT } from "unist-util-visit"; + +import { Image } from "./HtmlToMdResult.js"; +import { ObjectsInv } from "./objectsInv.js"; +import { Pkg } from "./Pkg.js"; +import { kebabCaseAndShortenPage } from "./normalizeResultUrls.js"; +import { relativizeLink } from "./updateLinks.js"; +import { transformSpecialCaseUrl } from "./specialCaseResults.js"; +import { parseMarkdown, extractHeadingText } from "../markdownUtils.js"; +import { NotebookCell, NotebookWithUrl } from "./Notebooks.js"; + +export async function readNotebooks( + artifactPath: string, + docsBaseFolder: string, + outputPath: string, + filePaths: string[], +): Promise { + const results: NotebookWithUrl[] = []; + for (const file of filePaths) { + const raw = await readFile(`${artifactPath}/${file}`, "utf-8"); + const notebook = JSON.parse(raw); + const { dir, name } = parse(`${outputPath}/${file}`); + const url = `/${relative(docsBaseFolder, dir)}/${name}`; + results.push({ ...notebook, url }); + } + return results; +} + +/** + * Rewrite markdown-cell links in each notebook: relativize old doc URLs and + * resolve `qiskit.github.io/{pkg}/stubs/...` links via the published-API + * inventories. Then prepend a frontmatter cell with a title extracted from + * the first markdown h1. + */ +export function processNotebooks( + notebooks: NotebookWithUrl[], + objectsInv: ObjectsInv, + allInvs: Map, + pkg: Pkg, + imageDestination: string, +): NotebookWithUrl[] { + return notebooks.map((notebook) => { + const processedCells = notebook.cells.map((cell) => { + if (cell.cell_type !== "markdown") return cell; + const linked = rewriteNotebookLinks(cell.source, objectsInv, allInvs, imageDestination); + const escaped = escapeMdxSpecialChars(linked); + const source = stripInlineStyles(escaped); + return { ...cell, source }; + }); + + const frontmatterCell = buildFrontmatterCell(processedCells, pkg); + return { + ...notebook, + cells: frontmatterCell + ? [frontmatterCell, ...processedCells] + : processedCells, + }; + }); +} + +/** + * Extract images referenced in notebook markdown cells as `Image` objects + * so they can be passed to `copyImages` alongside HTML-derived images. + */ +export function collectNotebookImages( + notebooks: NotebookWithUrl[], + imageDestination: string, +): Image[] { + const seen = new Set(); + const images: Image[] = []; + for (const notebook of notebooks) { + for (const cell of notebook.cells) { + if (cell.cell_type !== "markdown") continue; + const text = Array.isArray(cell.source) + ? cell.source.join("") + : cell.source; + for (const match of text.matchAll(/!\[[^\]]*\]\(([^)]+)\)/g)) { + const src = match[1]; + if (src.startsWith("http://") || src.startsWith("https://")) continue; + const fileName = src.split("/").pop()!; + if (seen.has(fileName)) continue; + seen.add(fileName); + images.push({ + fileName, + dest: `${imageDestination}/${fileName}`, + originSrc: `_images/${fileName}`, + }); + } + } + } + return images; +} + +export async function writeNotebooks( + pkg: Pkg, + docsBaseFolder: string, + notebooks: NotebookWithUrl[], +): Promise { + for (const { url, ...notebook } of notebooks) { + const normalizedUrl = normalizeNotebookUrl(url, pkg); + const path = `${docsBaseFolder}${normalizedUrl}.ipynb`; + await mkdirp(dirname(path)); + await writeFile(path, JSON.stringify(notebook, null, 1)); + } +} + +function normalizeNotebookUrl(url: string, pkg: Pkg): string { + const parts = url.split("/"); + const filename = parts[parts.length - 1]; + const normalized = pkg.kebabCaseAndShortenUrls + ? kebabCaseAndShortenPage(filename, pkg.name) + : filename; + return transformSpecialCaseUrl([...parts.slice(0, -1), normalized].join("/")); +} + +// Escape bare < outside code spans, code blocks, math regions, and HTML tags so that +// MDX's JSX parser doesn't interpret them as tag openings (e.g. `0 <= x` → `0 <= x`). +// required for opt-mapper how-tos +function escapeMdxSpecialChars(source: string | string[]): string { + const text = Array.isArray(source) ? source.join("") : source; + const protected_ = /```[\s\S]*?```|`[^`]+`|\$\$[\s\S]*?\$\$|\$[^$\n]+?\$|<[a-zA-Z/!][^>]*>/g; + const parts = text.split(protected_); + const matches = [...text.matchAll(protected_)].map((m) => m[0]); + return parts.map((p, i) => p.replace(/]*?)\s+style="[^"]*"/g, "$1"); +} + +function rewriteNotebookLinks( + source: string | string[], + objectsInv: ObjectsInv, + allInvs: Map, + imageDestination: string, +): string | string[] { + const rewrite = (line: string) => { + return line.replace(/(!?)\[([^\]]*)\]\(([^)]+)\)/g, (_match, bang, text, url) => { + if (bang === "!") { + return `![${text}](${rewriteNotebookImageSrc(url, imageDestination)})`; + } + const relativized = relativizeLink({ url, text }); + if (relativized) url = relativized.url; + const stub = objectsInv.resolveStubUrl(url, allInvs); + if (stub) url = stub; + return `[${text}](${url})`; + }); + }; + + return Array.isArray(source) ? source.map(rewrite) : rewrite(source); +} + +/** + * Rewrite Sphinx artifact-relative image paths (e.g. `../_static/images/foo.png`) + * to the public docs image destination. External URLs are left unchanged. + */ +function rewriteNotebookImageSrc(src: string, imageDestination: string): string { + if (src.startsWith("http://") || src.startsWith("https://")) return src; + return `${imageDestination}/${src.split("/").pop()!}`; +} + +function buildFrontmatterCell( + cells: NotebookCell[], + pkg: Pkg, +): NotebookCell | undefined { + for (const cell of cells) { + if (cell.cell_type !== "markdown") continue; + const text = Array.isArray(cell.source) + ? cell.source.join("") + : cell.source; + const tree = parseMarkdown(text); + let title: string | undefined; + visit(tree, "heading", (node: any) => { + if (node.depth === 1 && !title) { + title = extractHeadingText(node).trim(); + return EXIT; + } + }); + if (title) { + return { + id: "frontmatter", // hardcoded so the id doesn't change across runs + cell_type: "markdown", + source: `---\ntitle: "${title}"\ndescription: "${title} for the latest version of ${pkg.title}"\n---`, + metadata: {}, + }; + } + } + return undefined; +} diff --git a/scripts/js/lib/api/objectsInv.ts b/scripts/js/lib/api/objectsInv.ts index 93100542c9b..7a842759f8d 100644 --- a/scripts/js/lib/api/objectsInv.ts +++ b/scripts/js/lib/api/objectsInv.ts @@ -16,7 +16,7 @@ import { join, dirname } from "path"; import { mkdirp } from "mkdirp"; import { removePrefix, removeSuffix } from "../stringUtils.js"; -import { C_API_BASE_PATH } from "./conversionPipeline.js"; +import { C_API_BASE_PATH } from "./paths.js"; import { PackageLanguage } from "./Pkg.js"; /** @@ -27,8 +27,8 @@ const ENTRIES_TO_EXCLUDE = [ /^genindex(\.html)?$/, /^py-modindex(\.html)?$/, /^search(\.html)?$/, - /^explanation(\.html)?(?=\/|#|$)/, - /^how_to(\.html)?(?=\/|#|$)/, + /^explanations?(\.html)?(?=\/|#|$)/, + /^how[-_]tos?(\.html)?(?=\/|#|$)/, /^tutorials(\.html)?(?=\/|#|$)/, /^migration_guides(\.html)?(?=\/|#|$)/, /^configuration(\.html)?(?=#|$)/, @@ -55,6 +55,18 @@ function shouldIncludeEntry( if (entry.name.startsWith("group__")) return false; if (entry.name.startsWith("struct_")) return false; + // std: entries are RST labels that don't correspond to published pages + // unless they point into apidocs/ or stubs/. + if ( + entry.domainAndRole.startsWith("std:") && + !entry.uri.startsWith("apidocs/") && + !entry.uri.startsWith("stubs/") && + !entry.name.startsWith("/apidocs/") && + !entry.name.startsWith("/stubs/") + ) { + return false; + } + // This happens during link checking. if (packageLanguage === "any") return true; @@ -123,6 +135,7 @@ export class ObjectsInv { ): ObjectsInvEntry | null { // Regex from sphinx source // https://github.com/sphinx-doc/sphinx/blob/2f60b44999d7e610d932529784f082fc1c6af989/sphinx/util/inventory.py#L115-L116 + if (line.trim() === "") return null; const parts = line.match(/(.+?)\s+(\S+)\s+(-?\d+)\s+?(\S*)\s+(.*)/); if (parts == null || parts.length != 6) { console.warn(`Error parsing line of objects.inv: ${line}`); @@ -190,6 +203,68 @@ export class ObjectsInv { return uri; } + /** + * Load all published objects.inv files from public/docs/api/ and return + * a map of package name → ObjectsInv. These inventories have already been + * normalized by the API pipeline so URIs are ready to use directly. + */ + static async loadPublishedApis( + publicBaseFolder: string, + ): Promise> { + const { readdir } = await import("fs/promises"); + const apiDir = join(publicBaseFolder, "api"); + const map = new Map(); + let pkgDirs: string[]; + try { + pkgDirs = await readdir(apiDir); + } catch { + return map; + } + await Promise.all( + pkgDirs.map(async (pkgName) => { + try { + const inv = await ObjectsInv.fromFile(join(apiDir, pkgName), "any"); + map.set(pkgName, inv); + } catch { + // No objects.inv for this package — skip. + } + }), + ); + return map; + } + + /** + * Resolve a qiskit.github.io/{stubs,apidocs,apidoc}/ URL to an + * internal docs path using the package's inventory. + * + * Pass allInvs (from loadPublishedApis) for cross-package resolution. + * Requires updateUris() to have been called on same-package inventory first. + */ + resolveStubUrl( + url: string, + allObjectInvs?: Map, + ): string | undefined { + const match = url.match( + /^https:\/\/qiskit\.github\.io\/([^/]+)\/(stubs|apidocs|apidoc)\/([^"#)\s]+?)(?:\.html)?(#.*)?$/, + ); + if (!match) return undefined; + const [, pkg, kind, symbol, anchor = ""] = match; + const inv = allObjectInvs?.get(pkg) ?? this; + if (kind === "stubs") { + // The entry URI already points at the correct header anchor for the + // symbol — the source anchor (if any) is redundant and gets dropped. + const entry = inv.entries.find((e) => e.name === symbol); + return entry ? `/docs/api/${pkg}/${entry.uri}` : undefined; + } + // Apidocs URLs reference a whole page; the std:doc entry (keyed + // `/`) has the clean page-level URI, and the source anchor + // carries through to the rendered page. + const entry = + inv.entries.find((e) => e.name === `${kind}/${symbol}`) ?? + inv.entries.find((e) => e.name === symbol); + return entry ? `/docs/api/${pkg}/${entry.uri}${anchor}` : undefined; + } + updateUris(transformLink: (uri: string) => string): void { for (const entry of this.entries) { entry.uri = entry.uri.replace(/\.html/, ""); diff --git a/scripts/js/lib/api/paths.ts b/scripts/js/lib/api/paths.ts new file mode 100644 index 00000000000..09929108005 --- /dev/null +++ b/scripts/js/lib/api/paths.ts @@ -0,0 +1,17 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Base path under which all generated docs live on the site. +export const DOCS_BASE_PATH = "/docs"; + +// Folder in the Sphinx artifact that contains all C API docs. +export const C_API_BASE_PATH = "cdoc" as const; diff --git a/scripts/js/lib/api/pipelineStages.ts b/scripts/js/lib/api/pipelineStages.ts new file mode 100644 index 00000000000..b125b755589 --- /dev/null +++ b/scripts/js/lib/api/pipelineStages.ts @@ -0,0 +1,214 @@ +// This code is a Qiskit project. +// +// (C) Copyright IBM 2026. +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +// Stages shared between the API docs pipeline (apiDocsPipeline.ts) and the +// addon docs pipeline (addonDocsPipeline.ts). Each pipeline orchestrates these +// stages differently; shared behavior lives here so the two pipelines do not +// drift. + +import { dirname, join, parse, relative } from "path"; +import { readFile, writeFile } from "fs/promises"; + +import { load } from "cheerio"; +import { mkdirp } from "mkdirp"; +import { uniqBy } from "lodash-es"; + +import { Image, HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; +import { ObjectsInv } from "./objectsInv.js"; +import { Pkg } from "./Pkg.js"; +import addFrontMatter from "./addFrontMatter.js"; +import { dedupeHtmlIdsFromResults } from "./dedupeHtmlIds.js"; +import { handleReleaseNotesFile } from "./releaseNotes.js"; +import { mergeClassMembers } from "./mergeClassMembers.js"; +import { normalizeResultUrls } from "./normalizeResultUrls.js"; +import { DOCS_BASE_PATH } from "./paths.js"; +import removeMathBlocksIndentation from "./removeMathBlocksIndentation.js"; +import { saveImages } from "./saveImages.js"; +import { specialCaseResults } from "./specialCaseResults.js"; +import { sphinxHtmlToMarkdown } from "./htmlToMd.js"; +import { updateLinks } from "./updateLinks.js"; + +export async function convertHtmlToMarkdown( + pkg: Pkg, + artifactPath: string, + docsBaseFolder: string, + outputPath: string, + filePaths: string[], + imageDestination: string, + extractfrontMatter?: boolean, +): Promise { + const results: HtmlToMdResultWithUrl[] = []; + for (const file of filePaths) { + const html = await readFile(join(artifactPath, file), "utf-8"); + const result = await sphinxHtmlToMarkdown({ + html, + fileName: file, + determineGithubUrl: pkg.determineGithubUrlFn(), + imageDestination, + releaseNotesTitle: pkg.releaseNotesTitle(), + hasSeparateReleaseNotes: pkg.hasSeparateReleaseNotes(), + isCApi: pkg.isCApi(), + hasRootNamespaceFile: pkg.hasRootNamespaceFile, + }); + + // Skip empty markdown (HTML redirects, etc.). + if (result.markdown == "") continue; + + const { dir, name } = parse(`${outputPath}/${file}`); + const url = `/${relative(docsBaseFolder, dir)}/${name}`; + + if (extractfrontMatter) { + // extracts front matter from html source rather than generating it in addFrontMatter.js + result.meta.hardcodedFrontmatter = extractHtmlFrontmatter(html, pkg, url); + } + results.push({ ...result, url }); + } + return results; +} + +/** + * Extract title/description frontmatter directly from the Sphinx HTML

    . + * Used by the addon pipeline (extractfrontMatter=true) so addon pages get + * human-readable titles rather than the auto-generated slugs that addFrontMatter.ts + * would derive from the URL. + */ +function extractHtmlFrontmatter(html: string, pkg: Pkg, url: string): string { + const $ = load(html); + const h1 = $("h1") + .first() + .clone() + .find("a.headerlink") + .remove() + .end() + .text() + .trim(); + const isRootIndex = url.endsWith(`/${pkg.name}/index`); + const description = isRootIndex + ? `Documentation for the latest version of ${pkg.title}` + : `${h1} for the latest version of ${pkg.title}`; + return [`title: "${h1}"`, `description: "${description}"`].join("\n"); +} + +/** + * Apply the shared post-processing pipeline to a set of results. + * Order is load-bearing — both pipelines call the stages in this sequence. + */ +export async function postProcess( + pkg: Pkg, + initialResults: HtmlToMdResultWithUrl[], + objectsInv: ObjectsInv | undefined, + allInvs?: Map, +): Promise { + const results = await mergeClassMembers(initialResults); + + normalizeResultUrls(results, { + kebabCaseAndShorten: pkg.kebabCaseAndShortenUrls, + pkgName: pkg.name, + }); + + specialCaseResults(results); + rewriteApiDocsLinks(results, pkg); + + await updateLinks( + results, + { + kebabCaseAndShorten: pkg.kebabCaseAndShortenUrls, + pkgName: pkg.name, + pkgOutputDir: pkg.apiOutputDir(DOCS_BASE_PATH), + }, + objectsInv, + allInvs, + ); + + await dedupeHtmlIdsFromResults(results); + removeMathBlocksIndentation(results); + addFrontMatter(results, pkg); + return results; +} + +function rewriteApiDocsLinks(results: HtmlToMdResultWithUrl[], pkg: Pkg) { + const apiBase = pkg.apiOutputDir(DOCS_BASE_PATH); + const githubIo = `https://qiskit.github.io/${pkg.name}`; + for (const result of results) { + result.markdown = result.markdown + .replace( + /\]\((?:\.\.\/)*?(apidocs|apidoc|stubs)\/([^)]+)\)/g, + `](${apiBase}/$2)`, + ) + // Release notes live under the API pipeline's output, even when + // referenced from addon guides/tutorials. Catches relative + // `release-notes.html`, github.io-absolute, and .html-less forms. + .replace( + new RegExp( + `\\]\\((?:${githubIo}/|(?:\\.\\./)*?)release[_-]notes(?:\\.html)?(#[^)]*)?\\)`, + "g", + ), + `](${apiBase}/release-notes$1)`, + ); + } +} + +// --------------------------------------------------------------------------- +// Writing outputs +// --------------------------------------------------------------------------- + +/** + * Write each markdown result to disk at `/.mdx`. + * Release notes are routed through `handleReleaseNotesFile` so the caller + * doesn't have to special-case them; the pipelines that don't produce release + * notes (addons excludes them via their glob) are unaffected. + */ +export async function writeMarkdownResults( + pkg: Pkg, + docsBaseFolder: string, + results: HtmlToMdResultWithUrl[], +) { + for (const result of results) { + const path = `${docsBaseFolder}${result.url}.mdx`; + if (path.endsWith("release-notes.mdx")) { + if (!pkg.releaseNotesConfig.enabled) continue; + + const shouldWriteResult = await handleReleaseNotesFile(result, pkg); + if (!shouldWriteResult) continue; + } + + await mkdirp(dirname(path)); + await writeFile(path, result.markdown); + } +} + +/** + * Copy images referenced by the results from the artifact's `_images/` folder + * into `destFolder`. The caller owns the destination path, which is how the + * API pipeline and the addon pipeline route images to different locations + * under `public/docs/images/`. + */ +export async function copyImages( + pkg: Pkg, + artifactPath: string, + destFolder: string, + results: HtmlToMdResultWithUrl[], + extraImages: Image[] = [], +) { + console.log("Saving images"); + const allImages = uniqBy( + [...results.flatMap((result) => result.images), ...extraImages], + (image) => image.fileName, + ); + await saveImages( + allImages, + `${artifactPath}/_images`, + destFolder, + pkg, + artifactPath, + ); +} diff --git a/scripts/js/lib/api/processHtml.test.ts b/scripts/js/lib/api/processHtml.test.ts index 656e97f3c01..4e8af72b1b9 100644 --- a/scripts/js/lib/api/processHtml.test.ts +++ b/scripts/js/lib/api/processHtml.test.ts @@ -37,15 +37,24 @@ test.describe("loadImages()", () => { const doc = CheerioDoc.load( `Logo`, ); - const images = loadImages(doc.$, doc.$main, "/my-images", false, false); + const images = loadImages( + doc.$, + doc.$main, + "/my-images", + false, + false, + "subdir/index.html", + ); expect(images).toEqual([ { fileName: "logo.png", dest: "/my-images/logo.avif", + originSrc: "_static/logo.png", }, { fileName: "view-page-source-icon.svg", dest: "/my-images/view-page-source-icon.svg", + originSrc: "_static/images/view-page-source-icon.svg", }, ]); doc.expectHtml( @@ -57,11 +66,19 @@ test.describe("loadImages()", () => { const doc = CheerioDoc.load( ``, ); - const images = loadImages(doc.$, doc.$main, "/my-images/0.45", true, false); + const images = loadImages( + doc.$, + doc.$main, + "/my-images/0.45", + true, + false, + "subdir/release-notes.html", + ); expect(images).toEqual([ { fileName: "view-page-source-icon.svg", dest: "/my-images/view-page-source-icon.svg", + originSrc: "_static/images/view-page-source-icon.svg", }, ]); doc.expectHtml(``); @@ -71,15 +88,69 @@ test.describe("loadImages()", () => { const doc = CheerioDoc.load( ``, ); - const images = loadImages(doc.$, doc.$main, "/my-images/0.45", true, true); + const images = loadImages( + doc.$, + doc.$main, + "/my-images/0.45", + true, + true, + "subdir/release-notes.html", + ); expect(images).toEqual([ { fileName: "view-page-source-icon.svg", dest: "/my-images/0.45/view-page-source-icon.svg", + originSrc: "_static/images/view-page-source-icon.svg", }, ]); doc.expectHtml(``); }); + + test("external image URLs are not rewritten", () => { + const doc = CheerioDoc.load( + `StarsLogo`, + ); + const images = loadImages( + doc.$, + doc.$main, + "/my-images", + false, + false, + "subdir/index.html", + ); + expect(images).toEqual([ + { + fileName: "logo.png", + dest: "/my-images/logo.avif", + originSrc: "_static/logo.png", + }, + ]); + doc.expectHtml( + `StarsLogo`, + ); + }); + + test("_static image (nbsphinx thumbnail) is resolved from artifact root", () => { + const doc = CheerioDoc.load( + ``, + ); + const images = loadImages( + doc.$, + doc.$main, + "/my-images", + false, + false, + "how-tos/index.html", + ); + expect(images).toEqual([ + { + fileName: "nbsphinx-no-thumbnail.svg", + dest: "/my-images/nbsphinx-no-thumbnail.svg", + originSrc: "_static/nbsphinx-no-thumbnail.svg", + }, + ]); + doc.expectHtml(``); + }); }); test("handleSphinxDesignCards()", () => { diff --git a/scripts/js/lib/api/processHtml.ts b/scripts/js/lib/api/processHtml.ts index eafa4942eac..4d5c115569b 100644 --- a/scripts/js/lib/api/processHtml.ts +++ b/scripts/js/lib/api/processHtml.ts @@ -67,6 +67,7 @@ export async function processHtml( imageDestination, isReleaseNotes, hasSeparateReleaseNotes, + fileName, ); if (isReleaseNotes) { renameAllH1s($, releaseNotesTitle); @@ -110,17 +111,28 @@ export function loadImages( imageDestination: string, isReleaseNotes: boolean, hasSeparateReleaseNotes: boolean, + htmlFileName: string, ): Image[] { return $main .find("img") .toArray() - .filter((img) => $(img).attr("src")) + .filter((img) => { + const src = $(img).attr("src"); + return src && !src.startsWith("http://") && !src.startsWith("https://"); + }) .map((img) => { const $img = $(img); + const src = $img.attr("src")!; - const fileName = $img.attr("src")!.split("/").pop()!; + const fileName = src.split("/").pop()!; const fileExtension = path.extname(fileName); + // Resolve the image's path relative to the artifact root so saveImages + // can find files in _static/ or other subdirectories, not just _images/. + const originSrc = path.normalize( + path.join(path.dirname(htmlFileName), src), + ); + // We convert PNG and JPG to AVIF for reduced file size. The image-copying // logic detects changed extensions and converts the files. let dest = [".png", ".jpg", ".jpeg"].includes(fileExtension) @@ -134,7 +146,7 @@ export function loadImages( } $img.attr("src", dest); - return { fileName, dest }; + return { fileName, dest, originSrc }; }); } @@ -241,10 +253,8 @@ function detectLanguage( ): string | null { const defaultLanguage = options.isCApi ? "c" : "python"; // Two levels up from `pre` should have class `highlight-` - const detectedLanguage = $pre - .parent() - .parent()[0] - .attribs.class.match(/(?<=highlight-)\w+/); + const grandparentClass = $pre.parent().parent()[0]?.attribs?.class ?? ""; + const detectedLanguage = grandparentClass.match(/(?<=highlight-)\w+/); if (!detectedLanguage) return defaultLanguage; const langName = detectedLanguage[0]; if (langName === "none") return null; @@ -526,6 +536,9 @@ export function preserveMathBlockWhitespace( .toArray() .map((el) => { const $el = $(el); + // Remove equation number labels — the anchor IDs are on the parent divs, + // not the eqno span, so links to equations still resolve correctly. + $el.find("span.eqno").remove(); $el.replaceWith(`
    ${$el.html()}
    `); }); } diff --git a/scripts/js/lib/api/releaseNotes.ts b/scripts/js/lib/api/releaseNotes.ts index 4198fa987ac..5fa57d584a8 100644 --- a/scripts/js/lib/api/releaseNotes.ts +++ b/scripts/js/lib/api/releaseNotes.ts @@ -19,7 +19,7 @@ import transformLinks from "transform-markdown-links"; import { pathExists } from "../fs.js"; import type { Pkg } from "./Pkg.js"; import type { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; -import { C_API_BASE_PATH, DOCS_BASE_PATH } from "./conversionPipeline.js"; +import { C_API_BASE_PATH, DOCS_BASE_PATH } from "./paths.js"; import { kebabCaseAndShortenPage } from "./normalizeResultUrls.js"; import { removePrefix } from "../stringUtils.js"; import { generateReleaseNotesEntry, TocEntry } from "./generateToc.js"; diff --git a/scripts/js/lib/api/saveImages.ts b/scripts/js/lib/api/saveImages.ts index 2cdff78ed94..da9e3c0b3ef 100644 --- a/scripts/js/lib/api/saveImages.ts +++ b/scripts/js/lib/api/saveImages.ts @@ -11,7 +11,7 @@ // that they have been altered from the originals. import { copyFile } from "fs/promises"; -import { extname } from "node:path"; +import { dirname, extname } from "node:path"; import pMap from "p-map"; import { $ } from "zx"; @@ -49,10 +49,10 @@ function skipReleaseNote(imgFileName: string, pkg: Pkg): boolean { export async function saveImages( images: Image[], originalImagesFolderPath: string, - publicBaseFolder: string, + destFolder: string, pkg: Pkg, + artifactPath?: string, ) { - const destFolder = pkg.outputDir(`${publicBaseFolder}/docs/images`); if (!(await pathExists(destFolder))) { await mkdirp(destFolder); } @@ -61,9 +61,22 @@ export async function saveImages( if (skipReleaseNote(img.fileName, pkg)) { return; } - const source = `${originalImagesFolderPath}/${img.fileName}`; - const dest = `${publicBaseFolder}/${img.dest}`; + // Prefer the resolved artifact-relative path (covers _static/, etc.), + // falling back to the legacy _images/ convention. + const source = + artifactPath && img.originSrc + ? `${artifactPath}/${img.originSrc}` + : `${originalImagesFolderPath}/${img.fileName}`; + // img.dest is set by loadImages() and includes the full image URL prefix + // (e.g. "/docs/images/api/qiskit/foo.avif"). We only need its basename to + // place the file inside destFolder. + const dest = `${destFolder}/${img.dest.split("/").pop()}`; + if (!(await pathExists(source))) { + console.warn(`Skipping missing image: ${source}`); + return; + } + await mkdirp(dirname(dest)); if (extname(source) === extname(dest)) { await copyFile(source, dest); } else { diff --git a/scripts/js/lib/api/specialCaseResults.ts b/scripts/js/lib/api/specialCaseResults.ts index b0f042c814c..f9abd3eb559 100644 --- a/scripts/js/lib/api/specialCaseResults.ts +++ b/scripts/js/lib/api/specialCaseResults.ts @@ -19,6 +19,7 @@ export function transformSpecialCaseUrl(url: string): string { .replace(/(?<=^|\/)release_notes(?=#|$)/g, "release-notes") .replace(/(?<=^|\/)terra(?=#|$)/g, "index") .replace(/(?<=^|\/)ibm-runtime(?=#|$)/g, "index") + .replace(/(?<=^|\/)main(?=#|$)/g, "index") ); } diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke-publishedapis/api/qiskit-addon-other/objects.inv b/scripts/js/lib/api/testdata/qiskit-addon-smoke-publishedapis/api/qiskit-addon-other/objects.inv new file mode 100644 index 00000000000..f2dc4077d36 Binary files /dev/null and b/scripts/js/lib/api/testdata/qiskit-addon-smoke-publishedapis/api/qiskit-addon-other/objects.inv differ diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/_images/smoke-diagram.avif b/scripts/js/lib/api/testdata/qiskit-addon-smoke/_images/smoke-diagram.avif new file mode 100644 index 00000000000..0314f501c87 Binary files /dev/null and b/scripts/js/lib/api/testdata/qiskit-addon-smoke/_images/smoke-diagram.avif differ diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/how_tos/foo.html b/scripts/js/lib/api/testdata/qiskit-addon-smoke/how_tos/foo.html new file mode 100644 index 00000000000..f32c3484b7f --- /dev/null +++ b/scripts/js/lib/api/testdata/qiskit-addon-smoke/how_tos/foo.html @@ -0,0 +1,18 @@ + + + + + How to foo - smoke 0.0 + + + + + + diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/index.html b/scripts/js/lib/api/testdata/qiskit-addon-smoke/index.html new file mode 100644 index 00000000000..4ed84edfb4f --- /dev/null +++ b/scripts/js/lib/api/testdata/qiskit-addon-smoke/index.html @@ -0,0 +1,17 @@ + + + + + Qiskit Addon Smoke - smoke 0.0 + + + + + + diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/install.html b/scripts/js/lib/api/testdata/qiskit-addon-smoke/install.html new file mode 100644 index 00000000000..6b94ccebc65 --- /dev/null +++ b/scripts/js/lib/api/testdata/qiskit-addon-smoke/install.html @@ -0,0 +1,16 @@ + + + + + Installation - smoke 0.0 + + + +
    +
    +

    Installation#

    +

    Install with pip. Back to the home page.

    +
    +
    + + diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/objects.inv b/scripts/js/lib/api/testdata/qiskit-addon-smoke/objects.inv new file mode 100644 index 00000000000..b7ec346b6b6 Binary files /dev/null and b/scripts/js/lib/api/testdata/qiskit-addon-smoke/objects.inv differ diff --git a/scripts/js/lib/api/testdata/qiskit-addon-smoke/tutorials/intro.ipynb b/scripts/js/lib/api/testdata/qiskit-addon-smoke/tutorials/intro.ipynb new file mode 100644 index 00000000000..bd6059d589b --- /dev/null +++ b/scripts/js/lib/api/testdata/qiskit-addon-smoke/tutorials/intro.ipynb @@ -0,0 +1,31 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Intro tutorial\n", + "\n", + "This notebook references [do_thing](https://qiskit.github.io/qiskit-addon-smoke/stubs/smoke.do_thing.html) from the current package and [other_thing](https://qiskit.github.io/qiskit-addon-other/stubs/other_thing.html) from a sibling package.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('hello from the smoke tutorial')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/js/lib/api/updateLinks.ts b/scripts/js/lib/api/updateLinks.ts index 009f4cb5e57..cba4371c07a 100644 --- a/scripts/js/lib/api/updateLinks.ts +++ b/scripts/js/lib/api/updateLinks.ts @@ -26,9 +26,10 @@ import { removePart, removePrefix, removeSuffix } from "../stringUtils.js"; import { HtmlToMdResultWithUrl } from "./HtmlToMdResult.js"; import { remarkStringifyOptions } from "./commonParserConfig.js"; import { ObjectsInv } from "./objectsInv.js"; +import { Pkg } from "./Pkg.js"; import { transformSpecialCaseUrl } from "./specialCaseResults.js"; import { kebabCaseAndShortenPage } from "./normalizeResultUrls.js"; -import { DOCS_BASE_PATH } from "./conversionPipeline.js"; +import { DOCS_BASE_PATH } from "./paths.js"; export interface Link { url: string; // Where the link goes @@ -177,6 +178,8 @@ export function normalizeUrl( } export function relativizeLink(link: Link): Link | undefined { + rewriteQiskitAddonLinks(link); + const priorPrefixToNewPrefix = new Map([ ["https://qiskit.org/documentation/apidoc/", "/api/qiskit"], ["https://qiskit.org/documentation/stubs/", "/api/qiskit"], @@ -188,12 +191,12 @@ export function relativizeLink(link: Link): Link | undefined { const priorPrefix = Array.from(priorPrefixToNewPrefix.keys()).find((prefix) => link.url.startsWith(prefix), ); - if (!priorPrefix) { - return; - } + if (!priorPrefix) return; + let [url, anchor] = link.url.split("#"); url = removePrefix(url, priorPrefix); url = removeSuffix(url, ".html"); + if (anchor && anchor !== url) { url = `${url}#${anchor}`; } @@ -204,6 +207,25 @@ export function relativizeLink(link: Link): Link | undefined { return { url: `/${relativeUrl}`, text: newText }; } +function rewriteQiskitAddonLinks(link: Link) { + if (!link.url.startsWith("https://qiskit.github.io/")) return; + + // github.io stubs/apidocs URLs are looked up via objects.inv by the caller + if (/\/(stubs|apidocs|apidoc)\//.test(link.url)) return; + + const rest = removePrefix(link.url, "https://qiskit.github.io/"); + const [addonName, ...pathParts] = rest.split("#")[0].split("/"); + if (!addonName || !Pkg.ADDON_NAMES.includes(addonName)) return; + + const anchor = rest.includes("#") ? rest.split("#")[1] : undefined; + const pagePath = pathParts.map((s) => removeSuffix(s, ".html")).join("/"); + const url = anchor + ? `/docs/addons/${addonName}/${pagePath}#${anchor}` + : `/docs/addons/${addonName}/${pagePath}`; + const newText = link.url === link.text ? url : undefined; + return { url, text: newText }; +} + export async function updateLinks( results: HtmlToMdResultWithUrl[], kwargs: { @@ -212,6 +234,7 @@ export async function updateLinks( pkgOutputDir: string; }, maybeObjectsInv?: ObjectsInv, + allObjectInvs?: Map, ): Promise { const resultsByName = keyBy(results, (result) => result.meta.apiName!); const itemNames = new Set(keys(resultsByName)); @@ -254,7 +277,13 @@ export async function updateLinks( textNode.value = relativizedLink.text; } } - + const resolvedStub = maybeObjectsInv?.resolveStubUrl( + node.url, + allObjectInvs, + ); + if (resolvedStub) { + node.url = resolvedStub; + } node.url = normalizeUrl(node.url, resultsByName, itemNames, kwargs); }); }) diff --git a/scripts/js/lib/links/FileBatch.ts b/scripts/js/lib/links/FileBatch.ts index 03415c286ee..925a94be0cd 100644 --- a/scripts/js/lib/links/FileBatch.ts +++ b/scripts/js/lib/links/FileBatch.ts @@ -118,7 +118,10 @@ export function addLinksToMap( links: Set, linksToOriginFiles: Map, ): void { - const ignoreUrlsRegex = new RegExp(ALWAYS_IGNORED_URL_REGEXES.join("|"), "i"); + const ignoreUrlsRegex = + ALWAYS_IGNORED_URL_REGEXES.length > 0 + ? new RegExp(ALWAYS_IGNORED_URL_REGEXES.join("|"), "i") + : null; if (IGNORED_FILES.has(filePath)) return; links.forEach((link) => { if ( @@ -126,7 +129,7 @@ export function addLinksToMap( ALWAYS_IGNORED_URL_PREFIXES.some((prefix) => link.startsWith(prefix)) || ALWAYS_IGNORED_URL_SUFFIXES.some((suffix) => link.endsWith(suffix)) || FILES_TO_IGNORES[filePath]?.includes(link) || - ignoreUrlsRegex.test(link) + ignoreUrlsRegex?.test(link) ) { return; } diff --git a/scripts/js/lib/links/ignores.ts b/scripts/js/lib/links/ignores.ts index 077d9d0322b..c07abe6977b 100644 --- a/scripts/js/lib/links/ignores.ts +++ b/scripts/js/lib/links/ignores.ts @@ -228,22 +228,7 @@ export const ALWAYS_IGNORED_URLS = new Set([ // Always ignored URL regexes - be careful using this // ----------------------------------------------------------------------------------- -function _addonsObjectsInvRegexes(): string[] { - // Addons have non-API docs in their Sphinx build that translate into invalid links - // we should ignore - return ["how-tos", "how_tos", "install", "index", "explanations"].flatMap( - (path) => [ - // Latest version - `\/api\/qiskit-addon-[^\/]+\/${path}(\/.*|#.*|$)`, - // Historical versions - `\/api\/qiskit-addon-[^\/]+\/[0-9]+\.[0-9]{1,2}\/${path}(\/.*|#.*|$)`, - ], - ); -} - -export const ALWAYS_IGNORED_URL_REGEXES: string[] = [ - ..._addonsObjectsInvRegexes(), -]; +export const ALWAYS_IGNORED_URL_REGEXES: string[] = []; // ----------------------------------------------------------------------------------- // Always ignored URL suffixes - be careful using this @@ -587,8 +572,65 @@ function _qiskitCRegexes(): FilesToIgnores { }; } -const FILES_TO_IGNORES__SHOULD_FIX: FilesToIgnores = - mergeFilesToIgnores(_qiskitCRegexes()); +function _addonContentLinksToFix(): FilesToIgnores { + // These links point to old addon-repo tutorial slugs that no longer exist. + // The addon source docs need to be updated to use the new paths. + return { + "docs/addons/qiskit-addon-sqd/index.mdx": [ + "tutorials/01-chemistry-hamiltonian", + "tutorials/index", + "/docs/en/tutorials/sample-based-quantum-diagonalization", + "/docs/en/tutorials/sample-based-krylov-quantum-diagonalization", + ], + "docs/addons/qiskit-addon-cutting/index.mdx": [ + "tutorials/index", + "tutorials/01-gate-cutting-to-reduce-circuit-width", + "tutorials/02-gate-cutting-to-reduce-circuit-depth", + "tutorials/03-wire-cutting-via-move-instruction", + "tutorials/04-automatic-cut-finding", + "./circuit_cutting/explanations/index-rst#overview-of-circuit-cutting", + "how-tos/how-to-specify-cut-wires", + "./circuit_cutting/explanation/index-rst#overview-of-circuit-cutting", + ], + "docs/addons/qiskit-addon-aqc-tensor/index.mdx": ["tutorials/index"], + "docs/addons/qiskit-addon-obp/how_tos/truncate-operator-terms.ipynb": [ + "/docs/api/qiskit/qiskit.circuit.QuantumCircuit#html", + ], + "docs/addons/qiskit-addon-obp/how_tos/simulating-circuits-with-obp.ipynb": [ + "/api/qiskit-ibm-runtime/noise-learner-noise-learner", + "/api/qiskit-ibm-runtime/noise-learner-result", + ], + "docs/addons/qiskit-addon-cutting/install.mdx": [ + "/docs/start/install#operating-system-support", + ], + "docs/addons/qiskit-addon-cutting/explanation/index.mdx": [ + "#equation-eq-qpd", + "how-tos/how-to-specify-cut-wires", + "/circuit_cutting/explanations/index-rst#overview-of-circuit-cutting", + ], + "docs/addons/qiskit-addon-cutting/how-tos/how-to-specify-cut-wires.ipynb": [ + "../tutorials/03_wire_cutting_via_move_instruction.ipynb", + ], + "docs/addons/qiskit-addon-cutting/how-tos/how-to-generate-exact-sampling-coefficients.ipynb": + ["../tutorials/01_gate_cutting_to_reduce_circuit_width.ipynb"], + "docs/api/pauli-prop/propagation.mdx": [ + "/docs/en/api/qiskit/qiskit.quantum_info.Clifford", + "/docs/en/api/qiskit/qiskit.quantum_info.Pauli#evolve", + ], + "docs/addons/qiskit-addon-opt-mapper/how_tos/index.mdx": [ + "01-optimization-problem#add-or-remove-linear,-quadratic,-and-higher-order-terms-to-and-from-the-constraints", + ], + "docs/addons/qiskit-paulice/index.mdx": [ + "tutorials/index", + "_images/paulice-png", + ], + }; +} + +const FILES_TO_IGNORES__SHOULD_FIX: FilesToIgnores = mergeFilesToIgnores( + _qiskitCRegexes(), + _addonContentLinksToFix(), +); export const FILES_TO_IGNORES: FilesToIgnores = mergeFilesToIgnores( FILES_TO_IGNORES__EXPECTED, diff --git a/scripts/notebook-normalizer/qiskit_docs_notebook_normalizer/__init__.py b/scripts/notebook-normalizer/qiskit_docs_notebook_normalizer/__init__.py index 61b782f42c1..a612bd8445c 100644 --- a/scripts/notebook-normalizer/qiskit_docs_notebook_normalizer/__init__.py +++ b/scripts/notebook-normalizer/qiskit_docs_notebook_normalizer/__init__.py @@ -87,6 +87,20 @@ def main(): raise SystemExit(1) +REQUIRED_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", + }, +} + + def normalize_notebook( nb: nbformat.NotebookNode, image_folder: Path, check_only: bool = False ) -> NormalizationResult: @@ -95,6 +109,13 @@ def normalize_notebook( """ images = [] change_made = False + + for key, value in REQUIRED_METADATA.items(): + if key not in nb.metadata: + change_made = True + if not check_only: + nb.metadata[key] = value + for cell_index, cell in enumerate(nb.cells): if cell.cell_type != "code": continue diff --git a/tox.ini b/tox.ini index 458d5c11ce6..b277fe6815f 100644 --- a/tox.ini +++ b/tox.ini @@ -22,10 +22,10 @@ commands = lint: squeaky --check --no-advice {posargs:docs learning} lint: python scripts/ci/check-for-version-info-cells.py lint: qiskit-docs-notebook-normalizer --check + fix: qiskit-docs-notebook-normalizer fix: squeaky {posargs:docs learning} fix: ruff format {posargs:docs learning} fix: ruff check --fix {posargs:docs learning} - fix: qiskit-docs-notebook-normalizer [testenv:tests] deps =