├── .github └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── .mailmap ├── CONTRIBUTING.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── pyproject.toml ├── qiskit_qubit_reuse ├── __init__.py ├── plugin.py ├── qubit_reuse.py └── qubit_reuse_greedy.py ├── requirements.txt └── setup.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI Jobs 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | ruff: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: chartboost/ruff-action@v1 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: psf/black@stable -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Qubit Reuse Plugin to PyPI 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build-n-publish: 10 | name: Build and publish Qubit Reuse Plugin to PyPI 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up Python 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: "3.11" 18 | - name: Install pypa/build 19 | run: >- 20 | python3 -m 21 | pip install 22 | build 23 | --user 24 | - name: Build a binary wheel and a source tarball 25 | run: >- 26 | python3 -m 27 | build 28 | --sdist 29 | --wheel 30 | --outdir dist/ 31 | - name: Publish distribution to PyPI 32 | if: startsWith(github.ref, 'refs/tags') 33 | uses: pypa/gh-action-pypi-publish@release/v1 34 | with: 35 | password: ${{ secrets.PYPI_API_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SDK config file 2 | Qconfig.py 3 | 4 | # ply outputs 5 | qiskit/qasm/parser.out 6 | 7 | # editor files 8 | .vscode/ 9 | .idea/ 10 | 11 | #standard python ignores follow 12 | 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | .pytest_cache/ 16 | *.py[cod] 17 | *$py.class 18 | 19 | # C extensions 20 | *.so 21 | 22 | # Distribution / packaging 23 | .Python 24 | env/ 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | .eggs/ 31 | parts/ 32 | sdist/ 33 | var/ 34 | wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *,cover 58 | .hypothesis/ 59 | test/python/*.log 60 | test/python/*.pdf 61 | test/python/*.prof 62 | .stestr/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # celery beat schedule file 92 | celerybeat-schedule 93 | 94 | # SageMath parsed files 95 | *.sage.py 96 | 97 | # dotenv 98 | .env 99 | 100 | # virtualenv 101 | .venv 102 | venv/ 103 | ENV/ 104 | 105 | # Spyder project settings 106 | .spyderproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | .DS_Store 112 | 113 | dummyscripts/* 114 | 115 | token.json 116 | 117 | tutorial/rst/_build/* 118 | 119 | test/python/test_qasm_python_simulator.pdf 120 | 121 | doc/_build/* 122 | 123 | doc/**/_autodoc 124 | 125 | qiskit/bin/* 126 | 127 | test/python/test_save.json 128 | 129 | test/python/*.tex 130 | 131 | out/* 132 | 133 | # CMake generates this file on the fly 134 | qiskit/providers/local/qasm_simulator_cpp 135 | qiskit/providers/legacysimulators/qasm_simulator_cpp 136 | 137 | src/qasm-simulator-cpp/test/qubit_vector_tests 138 | 139 | *.o 140 | 141 | # Cython pass 142 | qiskit/transpiler/passes/**/cython/**/*.cpp 143 | qiskit/quantum_info/states/cython/*.cpp 144 | 145 | docs/stubs/* 146 | 147 | # Notebook testing images 148 | test/ipynb/mpl/circuit/*.png 149 | test/ipynb/mpl/circuit/*.zip 150 | test/ipynb/mpl/circuit/result_test.json 151 | test/ipynb/mpl/graph/*.png 152 | test/ipynb/mpl/graph/*.zip 153 | test/ipynb/mpl/graph/result_test.json 154 | 155 | # Added by cargo 156 | 157 | /target 158 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> 2 | Danielle Odigie 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We follow the same guidelines as [Qiskit](https://qiskit.org/documentation/contributing_to_qiskit.html). All contributions must also follow these guidelines: 4 | 5 | ## Bugs and feature requests: 6 | 7 | Please use Github Issues for reporting bugs found within this plugin. The same goes for feature requests. 8 | 9 | ## Code Review 10 | 11 | All new coded feature proposals should be done via Pull Requests. All sumbissions will require review by one of the mantainers. 12 | 13 | ## Code formatting: 14 | 15 | This project uses [Black](https://pypi.org/project/black/) and [Ruff](https://beta.ruff.rs/docs/) for code formatting. 16 | To install and use Black: 17 | 18 | ```sh 19 | pip install black 20 | ``` 21 | For automated code formatting with black: 22 | ```sh 23 | black qiskit_qubit_reuse 24 | ``` 25 | For code formatting review, use: 26 | ```sh 27 | black --check qiskit_qubit_reuse 28 | ``` 29 | 30 | To install and use Ruff: 31 | ```sh 32 | pip install ruff 33 | ruff check . 34 | ``` -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 IBM and its contributors 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright 2017 IBM and its contributors. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qubit Reuse By Reset Plugin 2 | 3 | This repository contains an experimental transpiler pass called `qubit_reuse` which is executed at the end of the `init` stage of transpilation. This pass is based on: Matthew DeCross et al. "Qubit-reuse compilation with mid-circuit measurement and reset" [arXiv:2210.0.08039v1](https://arxiv.org/abs/2210.08039v1) 4 | 5 | ### Background 6 | 7 | Certain circuits can reduce the number of qubits required to produce results by resetting and re-using existent measured qubits. The order in which certain qubits are chosen is based on their **causal cones** and the order in which they are measured. 8 | 9 | #### Causal Cones 10 | 11 | Let's say we have qubit a x in a `DAGCircuit`. We can traverse the `DAGCircuit` from the output node of x by checking all its predecessor nodes. When checking every operation node found, if at any point x interacts with other qubits, via a multi-qubit gate, the qubits in that operation are added to a set. From that point we continue evaluating recursively all the predecessor nodes in that multi-qubit interaction and adding all qubits found into the set, until no more predecessor nodes are left. 12 | 13 | When the traversal ends, the set will contain **all the qubits whose interactions affect qubit x**. That is what we call the causal cone of x. 14 | 15 | #### Order of Measurement 16 | 17 | Qubits are re-arranged based on the length of their causal cones in ascending order, i.e. the first to be re-arranged are those with smaller causal cones. 18 | 19 | Before re-arranging a qubit, we need to check if there are any qubit that have been measured and is available to re-use. If so, we reset it and apply all operations onto its wire. Otherwise, a new qubit is added and the operations are passed on to that wire. 20 | 21 | ## Installation 22 | 23 | This package is not available through pypi, but can be installed by cloning this repository: 24 | 25 | ```zsh 26 | git clone https://github.com/qiskit-community/qiskit-qubit-reuse 27 | ``` 28 | And then installing locally: 29 | 30 | ```zsh 31 | pip install ./qiskit-qubit-reuse 32 | ``` 33 | If you have the proper authentication keys, you can install it remotely by using: 34 | 35 | ```zsh 36 | pip install git+https://github.com/qiskit-community/qiskit-qubit-reuse 37 | ``` 38 | 39 | ## Usage 40 | 41 | Once installed, Qiskit is able to detect the `qubit_reuse` plugin via an entry point. All that needs to be done is to specify the init method in your `transpile` call by using `init_method="qubit_reuse"`. Use the following example: 42 | 43 | ```py3 44 | from qiskit.circuit.random import random_circuit 45 | from qiskit import transpile 46 | from qiskit.providers.fake_provider import GenericBackendV2 47 | 48 | qc = random_circuit(16, 4, measure=True) 49 | 50 | transpiled_qc = transpile(qc, backend=GenericBackendV2(16), init_method="qubit_reuse") 51 | ``` 52 | 53 | This entry point provides the option with the least amount of qubits. If you want to specifically use the normal or dual circuit, you can specifcy that by using the `qubit_reuse_normal` or the `qubit_reuse_dual` endpoints. 54 | 55 | **Warning: This plugin should only be used with circuits that contain measurements.** 56 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | line-length = 100 3 | target-version = "py311" 4 | 5 | [tool.black] 6 | line-length = 100 7 | target-version = ['py311'] 8 | -------------------------------------------------------------------------------- /qiskit_qubit_reuse/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/qiskit-qubit-reuse/a606c2feb0f9e2670d008c517eb7fb9833f34838/qiskit_qubit_reuse/__init__.py -------------------------------------------------------------------------------- /qiskit_qubit_reuse/plugin.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Qubit Reuse Init plugin.""" 14 | 15 | from qiskit.transpiler import PassManager 16 | from qiskit.transpiler.passes import UnitarySynthesis, Unroll3qOrMore 17 | from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager 18 | from qiskit.transpiler.preset_passmanagers.plugin import PassManagerStagePlugin 19 | 20 | from .qubit_reuse import QubitReuse 21 | 22 | 23 | def generate_optimization_manager(pass_manager_config, optimization_level=None, type="default"): 24 | # Build init state depending on the configs passed. Extract init 25 | preset_stage = generate_preset_pass_manager( 26 | optimization_level=optimization_level, 27 | target=pass_manager_config.target, 28 | basis_gates=pass_manager_config.basis_gates, 29 | ) 30 | # Try and get init attribute, if nonexistent, create a regular pass 31 | plugin_stage = getattr(preset_stage, "init", None) or PassManager( 32 | [ 33 | UnitarySynthesis( 34 | target=pass_manager_config.target, 35 | basis_gates=pass_manager_config.basis_gates, 36 | ), 37 | Unroll3qOrMore( 38 | target=pass_manager_config.target, 39 | basis_gates=pass_manager_config.basis_gates, 40 | ), 41 | ] 42 | ) 43 | # Append qubit reuse. 44 | plugin_stage.append(QubitReuse(target=pass_manager_config.target, type=type)) 45 | return plugin_stage 46 | 47 | 48 | class QubitReusePluginDefault(PassManagerStagePlugin): 49 | """Plugin for using Qubit Reset and Reuse in Default Mode.""" 50 | 51 | def pass_manager(self, pass_manager_config, optimization_level=None): 52 | """build qubit reuse init plugin stage pass manager. Default Mode.""" 53 | return generate_optimization_manager( 54 | pass_manager_config=pass_manager_config, 55 | optimization_level=optimization_level, 56 | ) 57 | 58 | 59 | class QubitReusePluginNormal(PassManagerStagePlugin): 60 | """Plugin for using Qubit Reset and Reuse.""" 61 | 62 | def pass_manager(self, pass_manager_config, optimization_level=None): 63 | """build qubit reuse init plugin stage pass manager in Normal Mode.""" 64 | return generate_optimization_manager( 65 | pass_manager_config=pass_manager_config, 66 | optimization_level=optimization_level, 67 | type="normal", 68 | ) 69 | 70 | 71 | class QubitReusePluginDual(PassManagerStagePlugin): 72 | """Plugin for using Qubit Reset and Reuse. Dual Mode.""" 73 | 74 | def pass_manager(self, pass_manager_config, optimization_level=None): 75 | """build qubit reuse init plugin stage pass manager in Dual Mode.""" 76 | return generate_optimization_manager( 77 | pass_manager_config=pass_manager_config, 78 | optimization_level=optimization_level, 79 | type="dual", 80 | ) 81 | -------------------------------------------------------------------------------- /qiskit_qubit_reuse/qubit_reuse.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """QubitReuse pass to reduce the number of qubits on an QuantumCircuit""" 14 | import logging 15 | 16 | from qiskit.transpiler.basepasses import TransformationPass 17 | from .qubit_reuse_greedy import Greedy 18 | 19 | logger = logging.getLogger(__name__) 20 | 21 | 22 | class QubitReuse(TransformationPass): 23 | """A qubit reuse via midcircuit measurement transformation pass 24 | 25 | Cite paper here 26 | """ 27 | 28 | def __init__(self, target, type="default"): 29 | """Initialize a ``QubitReuse`` pass instance 30 | 31 | Args: 32 | target (Target): A target representing the backend device to run ``QubitReuse`` on. 33 | type (str): A string specifying which method will be used. Defaults to "default". 34 | 35 | **References:** 36 | 37 | [1] Matthew DeCross et al. "Qubit-reuse compilation with mid-circuit measurement and reset" 38 | `arXiv:2210.0.08039v1 `_ 39 | """ 40 | super().__init__() 41 | self.target = target 42 | self.type = type 43 | 44 | def run(self, dag): 45 | """run the qubit reuse pass method""" 46 | if self.type == "dual": 47 | result = Greedy(dag=dag, dual=True) 48 | elif self.type == "normal": 49 | result = Greedy(dag) 50 | else: 51 | regular = Greedy(dag=dag) 52 | dual = Greedy(dag=dag, dual=True) 53 | regular_qubits = regular.dag.num_qubits() 54 | dual_qubits = dual.dag.num_qubits() 55 | 56 | result = regular if regular_qubits <= dual_qubits else dual 57 | 58 | return result.dag 59 | -------------------------------------------------------------------------------- /qiskit_qubit_reuse/qubit_reuse_greedy.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2022. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Greedy class to generate the qubit-reduced DAGCircuit""" 14 | 15 | import copy 16 | from qiskit.dagcircuit import DAGCircuit, DAGOpNode, DAGOutNode 17 | from qiskit.circuit import QuantumRegister, Qubit, ClassicalRegister, Clbit 18 | from qiskit.circuit.library import Reset 19 | 20 | 21 | class Greedy: 22 | def __init__(self, dag: DAGCircuit, dual: bool = False) -> None: 23 | # Public variables 24 | self.dag = DAGCircuit() 25 | 26 | # Private variables 27 | self.__dual = dual 28 | self.__dag: DAGCircuit = dag.reverse_ops() if self.__dual else dag 29 | self.__creg = ClassicalRegister(self.__dag.num_clbits()) 30 | self.__causal_cones: dict[int, set[Qubit]] = self.__get_causal_cones() 31 | self.__qubit_indices: dict[Qubit, int] = { 32 | qubit: i for i, qubit in enumerate(self.__dag.qubits) 33 | } 34 | self.__cl_indices: dict[Clbit, int] = { 35 | clbit: i for i, clbit in enumerate(self.__dag.clbits) 36 | } 37 | self.__qubit_mapping: dict[int, int] = {} 38 | self.__measured_qubits: list[int] = [] 39 | self.__visited_nodes: set[DAGOpNode] = set() 40 | self.__current_added_qubit: int = 0 41 | 42 | # Initialization 43 | self.dag.add_creg(self.__creg) 44 | for index, _ in self.__causal_cones.items(): 45 | self.__create_subpath(qubit=index) 46 | 47 | self.__qreg = QuantumRegister(bits=self.dag.qubits, name="q") 48 | self.dag.add_qreg(self.__qreg) 49 | 50 | if self.__dual: 51 | self.dag = self.dag.reverse_ops() 52 | 53 | def __get_causal_cones(self) -> dict[int, set[Qubit]]: 54 | """ 55 | Returns a sorted dictionary with each qubit as key and 56 | their respective causal cone as value. 57 | """ 58 | result = dict( 59 | sorted( 60 | list( 61 | { 62 | index: self.__dag.quantum_causal_cone(qubit) 63 | for index, qubit in enumerate(self.__dag.qubits) 64 | }.items() 65 | ), 66 | key=lambda item: len(item[1]), 67 | ) 68 | ) 69 | return result 70 | 71 | def __assign_qubit(self, index) -> None: 72 | """ 73 | Check if a new qubit from the new graph needs to be 74 | assigned to a qubit from the old circuit. 75 | 76 | If so, it either picks from a measured qubit or adds a new one to the circuit. 77 | """ 78 | if self.__qubit_mapping.get(index, None) is None: # In case the qubit hasn't been assigned 79 | # Case measure is available 80 | if len(self.__measured_qubits) > 0: 81 | # Collect from the measured qubits queue. 82 | new_index = self.__measured_qubits.pop(0) 83 | # Applies a reset operation to set qubit. 84 | self.dag.apply_operation_back( 85 | op=Reset(), qargs=(self.dag.qubits[new_index],), cargs=() 86 | ) 87 | # Map this qubit to the new one. 88 | self.__qubit_mapping[index] = new_index 89 | else: # Case no measured qubits available 90 | # Map the new qubit to the index from the current_added qubit 91 | self.__qubit_mapping[index] = self.__current_added_qubit 92 | # Increase latest added qubit index. 93 | self.__current_added_qubit += 1 94 | # Add a new qubit to the dag. 95 | self.dag.add_qubits([Qubit()]) 96 | 97 | def __create_subpath(self, qubit: Qubit | int, until_node: DAGOpNode | None = None) -> None: 98 | """ 99 | Recursively creates a subpath for a qubit in the circuit, based on its causal cone. 100 | """ 101 | # If the provided qubit is an instance of Qubit, proceed to assign 102 | if isinstance(qubit, Qubit): 103 | self.__assign_qubit(self.__qubit_indices[qubit]) 104 | # Else assign by index and retrieve the qubit index 105 | else: 106 | self.__assign_qubit(qubit) 107 | qubit = list(self.__qubit_indices)[qubit] 108 | # Get all nodes on wire 109 | queue = list(self.__dag.nodes_on_wire(qubit)) 110 | for index, current_node in enumerate(queue): 111 | if current_node == until_node: # Stop if we have reached the until node. 112 | break 113 | # If the current node has not been visited and is an instance of OpNode 114 | if current_node not in self.__visited_nodes and isinstance(current_node, DAGOpNode): 115 | # Add to the set of visited nodes. 116 | self.__visited_nodes.add(current_node) 117 | # Check if any of the qubits in qargs has not been added to the reduced circuit. 118 | if not current_node.op.name == "barrier": 119 | for op_qubit in current_node.qargs: 120 | self.__create_subpath(op_qubit, until_node=current_node) 121 | # Apply the operation, check for condition 122 | if hasattr(current_node.op, "condition"): 123 | oper = copy.copy(current_node.op) 124 | oper.condition = (self.__creg, oper.condition[1]) 125 | else: 126 | oper = current_node.op 127 | new_qargs = tuple( 128 | self.dag.qubits[self.__qubit_mapping[self.__qubit_indices[qbit]]] 129 | for qbit in current_node.qargs 130 | ) 131 | new_cargs = tuple( 132 | self.dag.clbits[self.__cl_indices[clbit]] for clbit in current_node.cargs 133 | ) 134 | self.dag.apply_operation_back( 135 | op=oper, 136 | qargs=new_qargs, 137 | cargs=new_cargs, 138 | ) 139 | 140 | # If measuring, add the qubit to the list of available qubits. 141 | if ( 142 | not self.__dual 143 | and current_node.op.name == "measure" 144 | and (len(queue) > index + 1 and isinstance(queue[index + 1], DAGOutNode)) 145 | ): 146 | self.__measured_qubits.append( 147 | self.__qubit_mapping[self.__qubit_indices[current_node.qargs[0]]] 148 | ) 149 | elif ( 150 | self.__dual 151 | and isinstance(current_node, DAGOutNode) 152 | and isinstance(current_node.wire, Qubit) 153 | ): 154 | self.__measured_qubits.append( 155 | self.__qubit_mapping[self.__qubit_indices[current_node.wire]] 156 | ) 157 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | qiskit>=0.43.0 2 | rustworkx>=0.12.0 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # This code is part of Qiskit. 2 | # 3 | # (C) Copyright IBM 2017. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """The Qiskit Qubit Reuse setup file.""" 14 | 15 | import os 16 | import re 17 | from setuptools import setup, find_packages 18 | 19 | 20 | with open("requirements.txt") as f: 21 | REQUIREMENTS = f.read().splitlines() 22 | 23 | # Read long description from README. 24 | README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") 25 | with open(README_PATH) as readme_file: 26 | README = re.sub( 27 | ".*", 28 | "", 29 | readme_file.read(), 30 | flags=re.S | re.M, 31 | ) 32 | 33 | setup( 34 | name="qiskit-qubit-reuse", 35 | version="0.0.2", 36 | description="A Qiskit's compiler plugin to reuse qubits using midcircuit measurement", 37 | long_description=README, 38 | long_description_content_type="text/markdown", 39 | author="Raynel Sanchez and Danielle Odigie", 40 | license="Apache 2.0", 41 | classifiers=[ 42 | "Environment :: Console", 43 | "License :: OSI Approved :: Apache Software License", 44 | "Intended Audience :: Developers", 45 | "Intended Audience :: Science/Research", 46 | "Operating System :: Microsoft :: Windows", 47 | "Operating System :: MacOS", 48 | "Operating System :: POSIX :: Linux", 49 | "Programming Language :: Python :: 3 :: Only", 50 | "Programming Language :: Python :: 3.8", 51 | "Programming Language :: Python :: 3.9", 52 | "Programming Language :: Python :: 3.10", 53 | "Programming Language :: Python :: 3.11", 54 | "Topic :: Scientific/Engineering", 55 | ], 56 | keywords="qiskit sdk quantum", 57 | packages=find_packages(exclude=["test*"]), 58 | install_requires=REQUIREMENTS, 59 | include_package_data=True, 60 | python_requires=">=3.8", 61 | zip_safe=False, 62 | entry_points={ 63 | "qiskit.transpiler.init": [ 64 | "qubit_reuse = qiskit_qubit_reuse.plugin:QubitReusePluginDefault", 65 | "qubit_reuse_normal = qiskit_qubit_reuse.plugin:QubitReusePluginNormal", 66 | "qubit_reuse_dual = qiskit_qubit_reuse.plugin:QubitReusePluginDual", 67 | ], 68 | }, 69 | ) 70 | --------------------------------------------------------------------------------