├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── documentation.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .mailmap ├── .travis.yml ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── MANIFEST.in ├── Make.bat ├── Makefile ├── README.md ├── VERSION.txt ├── cmake └── Modules │ ├── FindMPFR++.cmake │ └── FindMPFR.cmake ├── examples ├── bell_state.py ├── jku_backends.py └── two_register.py ├── jku_tcad.bib ├── qiskit_jku_provider ├── __init__.py ├── jkujob.py ├── jkuprovider.py ├── jkusimulatorerror.py └── qasm_simulator_jku.py ├── requirements-dev.txt ├── setup.py ├── src ├── QASMscanner.cpp ├── QASMscanner.hpp ├── QASMsimulator.cpp ├── QASMsimulator.h ├── QASMtoken.cpp ├── QASMtoken.hpp ├── QMDDcircuit.cpp ├── QMDDcircuit.h ├── QMDDcomplex.h ├── QMDDcomplexD.cpp ├── QMDDcore.h ├── QMDDpackage.cpp ├── QMDDpackage.h ├── QMDDreorder.cpp ├── QMDDreorder.h ├── Simulator.cpp ├── Simulator.h ├── external.h ├── main.cpp ├── qcost.cpp ├── qcost.h ├── textFileUtilities.cpp ├── textFileUtilities.h ├── timing.cpp └── timing.h └── test ├── __init__.py ├── _random_circuit_generator.py ├── common.py ├── qasms └── example.qasm ├── test_jku_backend.py ├── test_jku_provider.py ├── test_jku_snapshot.py ├── test_multi_registers_convention.py ├── test_qasm_simulator.py └── test_qasm_simulator_jku.py /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at qiskit@qiskit.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you would like to contribute to the qiskit-jku-provider, there are a number of ways to 4 | get involved: 5 | 6 | * **Issues**: Issues can be reported with GitHub [issue 7 | reporting](https://github.com/Qiskit/qiskit-jku-provider/issues) for this repository. 8 | Select `New issue`, fill in a descriptive title, and provide as much detail 9 | as is needed for the issue to be reproduced. 10 | 11 | * **Code and notebooks**: If you would like to contribute code or a notebook, please 12 | create a [fork](https://help.github.com/articles/fork-a-repo/) of the repository 13 | from the `master` branch and create a 14 | [pull request](https://help.github.com/articles/about-pull-requests) for your change. 15 | New notebooks may be placed in the 16 | [examples](../examples/) section. 17 | 18 | ## Contributor License Agreement 19 | 20 | We'd love to accept your code! Before we can, we have to get a few legal 21 | requirements sorted out. By having you sign a Contributor License Agreement (CLA), we 22 | ensure that the community is free to use your contributions. 23 | 24 | When you contribute to the Qiskit project with a new pull request, a bot will 25 | evaluate whether you have signed the CLA. If required, the bot will comment on 26 | the pull request, including a link to accept the agreement. The 27 | [individual CLA](https://qiskit.org/license/qiskit-cla.pdf) document is 28 | available for review as a PDF. 29 | 30 | If you work for a company that wants to allow you to contribute your work, 31 | then you'll need to sign a [corporate CLA](https://qiskit.org/license/qiskit-corporate-cla.pdf) 32 | and email it to us at qiskit@qiskit.org. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: "Create a report to help us improve \U0001F914." 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | ### Informations 14 | 15 | - **Qiskit-JKU-Provider version**: 16 | - **Python version**: 17 | - **Operating system**: 18 | 19 | ### What is the current behavior? 20 | 21 | 22 | 23 | ### Steps to reproduce the problem 24 | 25 | 26 | 27 | ### What is the expected behavior? 28 | 29 | 30 | 31 | ### Suggested solutions 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation 3 | about: 'Create a report to help us improve the documentation ' 4 | title: '' 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: "Suggest an idea for this project \U0001F4A1!" 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | ### What is the expected behavior? 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ### Summary 13 | 14 | 15 | 16 | ### Details and comments 17 | 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # Entries in this file are made for two reasons: 2 | # 1) to merge multiple git commit authors that correspond to a single author 3 | # 2) to change the canonical name and/or email address of an author. 4 | # 5 | # Format is: 6 | # Canonical Name commit name 7 | # \--------------+---------------/ \----------+-------------/ 8 | # replace find 9 | # See also: 'git shortlog --help' and 'git check-mailmap --help'. 10 | # 11 | # If you don't like the way your name is cited by qiskit, please feel free to 12 | # open a pull request against this file to set your preferred naming. 13 | # 14 | # Note that each qiskit element uses its own mailmap so it may be necessary to 15 | # propagate changes in other repos for consistency. 16 | # 17 | Alwin Zulehner 18 | Diego M. Rodríguez 19 | Gadi Aleksandrowicz 20 | Jay M. Gambetta 21 | Juan Gomez-Mosquera 22 | Juan Gomez-Mosquera 23 | Matthew Treinish 24 | Robert Wille 25 | Paul Kassebaum 26 | Yehuda Naveh 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2019, IBM. 2 | # 3 | # This source code is licensed under the Apache License, Version 2.0 found in 4 | # the LICENSE.txt file in the root directory of this source tree. 5 | 6 | dist: xenial 7 | 8 | language: python 9 | 10 | python: 11 | - "3.5" 12 | 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - libboost-program-options-dev 19 | - libmpfrc++-dev 20 | 21 | install: 22 | - pip install "qiskit>=0.8" 23 | - pip install -r requirements-dev.txt 24 | 25 | matrix: 26 | include: 27 | - name: style 28 | script: 29 | - make style 30 | - name: test Python 3.5 31 | script: 32 | - make sim 33 | - make test 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). 7 | 8 | **Types of changes:** 9 | 10 | - **Added**: for new features. 11 | - **Changed**: for changes in existing functionality. 12 | - **Deprecated**: for soon-to-be removed features. 13 | - **Removed**: for now removed features. 14 | - **Fixed**: for any bug fixes. 15 | - **Security**: in case of vulnerabilities. 16 | 17 | 18 | ## [UNRELEASED] 19 | 20 | ### Added 21 | 22 | ### Changed 23 | 24 | ### Removed 25 | 26 | 27 | ## [0.1.0] - 2019-02-21 28 | 29 | ### Added 30 | 31 | - Initial public release, including the JKU Simulator as a provider and backend 32 | fully compatible with Qiskit Terra 0.7. 33 | 34 | 35 | [UNRELEASED]: https://github.com/Qiskit/qiskit-jku-provider/compare/0.1.0...HEAD 36 | [0.1.0]: https://github.com/Qiskit/qiskit-jku-provider/compare/9b89a18...0.1.0 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | project(jku_simulator LANGUAGES CXX) 3 | 4 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 5 | list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 6 | 7 | # Set default build type to Release 8 | IF(NOT CMAKE_BUILD_TYPE) 9 | SET(CMAKE_BUILD_TYPE Release CACHE STRING 10 | "Choose the type of build, options are: Debug Release" 11 | FORCE) 12 | ENDIF() 13 | 14 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_REENTRANT -DVERSION=\"$(VERSION)\"") 15 | 16 | IF(STATIC_LINKING) 17 | IF(NOT APPLE) 18 | SET(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++") 19 | ENDIF() 20 | SET(CMAKE_FIND_LIBRARY_SUFFIXES .a) 21 | IF(WIN32) 22 | LIST(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a) 23 | ENDIF() 24 | ENDIF() 25 | 26 | IF(APPLE) 27 | SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "" FORCE) 28 | ENDIF() 29 | 30 | if(STATIC_LINKING) 31 | SET(Boost_USE_STATIC_LIBS ON) 32 | ENDIF() 33 | FIND_PACKAGE(Boost 1.50 COMPONENTS program_options REQUIRED) 34 | INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} ) 35 | 36 | FIND_PACKAGE(MPFR REQUIRED) 37 | FIND_PACKAGE(MPFR++ REQUIRED) 38 | INCLUDE_DIRECTORIES(${MPFR_INCLUDE_DIRS}) 39 | 40 | SET(JKU_LIBS 41 | ${MPFR_LIBRARIES} 42 | ${Boost_LIBRARIES}) 43 | 44 | add_executable(jku_simulator 45 | src/main.cpp 46 | src/Simulator.cpp 47 | src/QASMsimulator.cpp 48 | src/QASMscanner.cpp 49 | src/QASMtoken.cpp 50 | src/QMDDpackage.cpp 51 | src/textFileUtilities.cpp 52 | src/qcost.cpp 53 | src/timing.cpp 54 | src/QMDDcircuit.cpp 55 | src/QMDDcomplexD.cpp 56 | src/QMDDreorder.cpp) 57 | 58 | set_target_properties(jku_simulator PROPERTIES 59 | LINKER_LANGUAGE CXX 60 | CXX_STANDARD 14) 61 | 62 | IF(STATIC_LINKING) 63 | set_target_properties(jku_simulator PROPERTIES 64 | LINK_SEARCH_END_STATIC True) 65 | ENDIF() 66 | 67 | 68 | target_link_libraries(jku_simulator ${JKU_LIBS}) 69 | 70 | include_directories(src) 71 | 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 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 2018 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 qiskit/backends/jku/jku_simulator 2 | recursive-include src * 3 | recursive-include cmake * 4 | include CMakeLists.txt 5 | include setup.py 6 | -------------------------------------------------------------------------------- /Make.bat: -------------------------------------------------------------------------------- 1 | :: Copyright 2018, IBM. 2 | :: 3 | :: This source code is licensed under the Apache License, Version 2.0 found in 4 | :: the LICENSE.txt file in the root directory of this source tree. 5 | 6 | @ECHO OFF 7 | @SETLOCAL enabledelayedexpansion 8 | 9 | pushd %~dp0 10 | 11 | SET target=%~n1 12 | 13 | IF "%target%"=="lint" GOTO :lint 14 | IF "%target%"=="test" GOTO :test 15 | IF "%target%"=="profile" GOTO :profile 16 | IF "%target%"=="dist" GOTO :dist 17 | IF "%target%"=="sim" GOTO :sim 18 | IF "%target%"=="style" GOTO :style 19 | 20 | :usage 21 | ECHO. 22 | ECHO.Usage: 23 | ECHO. .\make lint Runs Pyhton source code analysis tool 24 | ECHO. .\make test Runs tests 25 | ECHO. .\make profile Runs profiling tests 26 | ECHO. .\make sim Builds the simulator 27 | ECHO. .\make style Checks code style 28 | ECHO. 29 | GOTO :end 30 | 31 | :lint 32 | pylint -rn qiskit\backends\jku test_jku_simulator 33 | IF errorlevel 9009 GOTO :error 34 | GOTO :next 35 | 36 | :test 37 | python -m unittest discover -s test_jku -v 38 | IF errorlevel 9009 GOTO :error 39 | GOTO :next 40 | 41 | :profile 42 | python -m unittest discover -p "profile*.py" -v 43 | IF errorlevel 9009 GOTO :error 44 | GOTO :next 45 | 46 | :dist 47 | FOR %%A IN (py34_64 py35_64 py36_64 py37_64 py34_32 py35_32 py36_32 py37_32) DO ( 48 | activate %%A 49 | python setup.py bdist_wheel 50 | ) 51 | IF errorlevel 9009 GOTO :error 52 | GOTO :next 53 | 54 | :sim 55 | cmake . -Bbuild/lib/qiskit_jku_provider -DSTATIC_LINKING=True -G"MinGW Makefiles" 56 | mingw32-make -C build/lib/qiskit_jku_provider VERBOSE=1 57 | COPY %MPFRDIR%\mpfr.dll build\lib\qiskit_jku_provider\mpfr.dll 58 | COPY %GMPDIR%\mpir.dll build\lib\qiskit_jku_provider\mpir.dll 59 | GOTO :next 60 | 61 | :style 62 | pycodestyle --max-line-length=100 qiskit_jku_provider test 63 | GOTO :next 64 | 65 | :error 66 | ECHO. 67 | ECHO.Somehting is missing in your Python installation. 68 | ECHO.Please make sure you have properly installed Anaconda3 69 | ECHO.(https://www.continuum.io/downloads), and that you are 70 | ECHO.running the "Anaconda Shell Command". 71 | ECHO. 72 | exit /b 1 73 | 74 | :next 75 | 76 | :end 77 | popd 78 | ENDLOCAL -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018, IBM. 2 | # 3 | # This source code is licensed under the Apache License, Version 2.0 found in 4 | # the LICENSE.txt file in the root directory of this source tree. 5 | SHELL := /bin/bash 6 | .PHONY: sim style lint test profile dist 7 | 8 | sim: 9 | cmake . -Bbuild/lib/qiskit_jku_provider -DSTATIC_LINKING=True 10 | make -C build/lib/qiskit_jku_provider 11 | 12 | lint: 13 | pylint -rn qiskit_jku_provider test 14 | 15 | style: 16 | pycodestyle --max-line-length=100 qiskit_jku_provider test 17 | 18 | test: 19 | python3 -m unittest discover 20 | 21 | profile: 22 | python3 -m unittest discover -p "profile*.py" -v 23 | 24 | dist: 25 | for env in py37 py36 py35 py34 ; do \ 26 | source activate $$env ; \ 27 | python setup.py bdist_wheel ; \ 28 | done 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qiskit JKU Simulator Provider is superseded by [JKQ DDSIM](https://github.com/iic-jku/ddsim) 2 | 3 | The Qiskit JKU Simulator is superseded by [JKQ DDSIM](https://github.com/iic-jku/ddsim), which uses a reworked 4 | implementation of the underlying decision diagrams as well as pybind11 (instead of writing OpenQASM code to files). 5 | Existing code can be adapted with minimal changes (import statements and provider name), new code should use the JKQ DDSIM 6 | from start. Python wheels are available via PyPI as [jkq.ddsim](https://pypi.org/project/jkq.ddsim/). 7 | 8 | The *Getting Started* example listed below would need the following changes: 9 | 10 | * `pip install qiskit-jku-provider` → `pip install jkq.ddsim` 11 | * `from qiskit_jku_provider import JKUProvider` → `from jkq import ddsim` 12 | * `JKU = JKUProvider()` → `JKU = ddsim.JKQProvider()` 13 | * Optional, but recommended: Rename variables containing `jku` to contain `jkq` instead. 14 | 15 | 16 | # Qiskit JKU Simulator Provider 17 | 18 | [![License](https://img.shields.io/github/license/Qiskit/qiskit-jku-provider.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0)[![Build Status](https://img.shields.io/travis/com/Qiskit/qiskit-jku-provider/master.svg?style=popout-square)](https://travis-ci.com/Qiskit/qiskit-jku-provider)[![](https://img.shields.io/github/release/Qiskit/qiskit-jku-provider.svg?style=popout-square)](https://github.com/Qiskit/qiskit-jku-provider/releases)[![](https://img.shields.io/pypi/dm/qiskit-jku-provider.svg?style=popout-square)](https://pypi.org/project/qiskit-jku-provider/) 19 | 20 | ## Simulate redundant quantum states efficiently 21 | 22 | This module contains the *Qiskit JKU Simulator Provider*, which gives access to the [JKU Simulator](http://iic.jku.at/eda/research/quantum_simulation/) as a backend. Together, they form a provider/backend pair in the [Qiskit backend interface framework](https://qiskit.org/documentation/advanced_use_of_ibm_q_devices.html). 23 | 24 | The JKU Simulator improves the runtime and memory consumption of classically simulated quantum circuits with redundant quantum states by using specially designed decision-diagram data structures. The JKU simulator was written by Alwin Zulehner and Robert Wille from Johannes Kepler Universität Linz. 25 | 26 | ## Installation 27 | 28 | Install [Qiskit](https://qiskit.org/) and this module with pip, the package installer for Python: 29 | 30 | ``` 31 | pip install qiskit 32 | pip install qiskit-jku-provider 33 | ``` 34 | 35 | pip will handle all dependencies automatically. 36 | 37 | ## Getting Started 38 | 39 | After installing `qiskit` and `qiskit-jku-provider`, the simulator can be used as demonstrated below. 40 | 41 | ```python 42 | # Import tools 43 | from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute 44 | from qiskit_jku_provider import JKUProvider 45 | 46 | # Create an instance of the JKU Provider 47 | JKU = JKUProvider() 48 | 49 | # Create a quantum circuit 50 | qr = QuantumRegister(2) 51 | cr = ClassicalRegister(2) 52 | qc = QuantumCircuit(qr, cr) 53 | 54 | # Add quantum gates 55 | qc.h(qr[0]) 56 | qc.cx(qr[0], qr[1]) 57 | qc.measure(qr, cr) 58 | 59 | # Get the JKU backend from the JKU provider 60 | jku_backend = JKU.get_backend('qasm_simulator') 61 | 62 | # Simulate the circuit with the JKU Simulator 63 | job = execute(qc, backend=jku_backend) 64 | 65 | # Retrieve and display the results 66 | result = job.result() 67 | print(result.get_counts(qc)) 68 | ``` 69 | ## Contribution Guidelines 70 | 71 | If you'd like to contribute to the JKU simulator, please take a look at our 72 | [contribution guidelines](.github/CONTRIBUTING.md). This project adheres to Qiskit's [code of conduct](.github/CODE_OF_CONDUCT.md). By participating, you are expect to uphold to this code. 73 | 74 | We use [GitHub issues](https://github.com/Qiskit/qiskit-jku-provider/issues) for tracking requests and bugs. Please use our [slack](https://qiskit.slack.com) for discussion and simple questions. To join our Slack community use the [link](https://join.slack.com/t/qiskit/shared_invite/enQtNDc2NjUzMjE4Mzc0LTMwZmE0YTM4ZThiNGJmODkzN2Y2NTNlMDIwYWNjYzA2ZmM1YTRlZGQ3OGM0NjcwMjZkZGE0MTA4MGQ1ZTVmYzk). For questions that are more suited for a forum we use the Qiskit tag in the [Stack Exchange](https://quantumcomputing.stackexchange.com/questions/tagged/qiskit). 75 | 76 | ## Next Steps 77 | 78 | Beyond running the JKU simulator, you may want to experiment and compare with other Qiskit simulators. You'll find the repository of different simulators at [Qiskit-Aer](https://github.com/Qiskit/qiskit-aer), or start with the 79 | [Qiskit-Aer tutorials](https://github.com/Qiskit/qiskit-tutorials/tree/master/qiskit/aer). Once you get the feel, you are welcome to contribute and make the JKU simulator even better. Or you can provide your own simulator and let others examine and contribute to it. For this, see [creating a new provider](https://github.com/Qiskit/qiskit-tutorials/blob/master/qiskit/terra/creating_a_provider.ipynb). 80 | 81 | ## Authors and Citation 82 | 83 | The JKU simulator was created by Alwin Zulehner and Robert Wille from the Johannes Kepler University in Linz, Austria. See [JKU Quantum](http://iic.jku.at/eda/research/quantum/) for more information on the team, or the full [TCAD paper](http://iic.jku.at/files/eda/2018_tcad_advanced_simulation_quantum_computations.pdf) ([bibtex](./jku_tcad.bib)) for a presentation of the underlying concepts and algorithm. 84 | 85 | The adaptation of the JKU simulator to Qiskit was done by Gadi Aleksandrowicz. If you use parts of Qiskit, please cite as per the included [BibTeX file](https://github.com/Qiskit/qiskit/blob/master/Qiskit.bib). 86 | 87 | ## License 88 | 89 | [Apache License 2.0](./LICENSE) 90 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 1.0.0 -------------------------------------------------------------------------------- /cmake/Modules/FindMPFR++.cmake: -------------------------------------------------------------------------------- 1 | # Try to find the MPFR C++ interface 2 | # See http://www.holoborodko.com/pavel/mpfr/ 3 | # 4 | # This module supports requiring a minimum version, e.g. you can do 5 | # find_package(MPFR++ 2.3.0) 6 | # to require version 2.3.0 to newer of MPFR C++. 7 | # 8 | # Once done this will define 9 | # 10 | # MPREAL_FOUND - system has MPFR C++ interface with correct version 11 | # MPREAL_INCLUDES - the MPFR C++ include directory 12 | # MPREAL_VERSION - MPFR C++ version 13 | 14 | # Copyright (c) 2006, 2007 Montel Laurent, 15 | # Copyright (c) 2008, 2009 Gael Guennebaud, 16 | # Copyright (c) 2010 Jitse Niesen, 17 | # Copyright (c) 2015 Jack Poulson, 18 | # Redistribution and use is allowed according to the terms of the BSD license. 19 | 20 | find_path(MPREAL_INCLUDES NAMES mpreal.h PATHS $ENV{MPFRDIR} ${INCLUDE_INSTALL_DIR}) 21 | 22 | # Set MPREAL_FIND_VERSION to 1.0.0 if no minimum version is specified 23 | if(NOT MPREAL_FIND_VERSION) 24 | if(NOT MPREAL_FIND_VERSION_MAJOR) 25 | set(MPREAL_FIND_VERSION_MAJOR 1) 26 | endif() 27 | if(NOT MPREAL_FIND_VERSION_MINOR) 28 | set(MPREAL_FIND_VERSION_MINOR 0) 29 | endif() 30 | if(NOT MPREAL_FIND_VERSION_PATCH) 31 | set(MPREAL_FIND_VERSION_PATCH 0) 32 | endif() 33 | set(MPREAL_FIND_VERSION 34 | "${MPREAL_FIND_VERSION_MAJOR}.${MPREAL_FIND_VERSION_MINOR}.${MPREAL_FIND_VERSION_PATCH}") 35 | endif() 36 | 37 | if(MPREAL_INCLUDES) 38 | # Query MPREAL_VERSION 39 | file(READ "${MPREAL_INCLUDES}/mpreal.h" _mpreal_version_header) 40 | 41 | string(REGEX MATCH "define[ \t]+MPREAL_VERSION_MAJOR[ \t]+([0-9]+)" 42 | _mpreal_major_version_match "${_mpreal_version_header}") 43 | set(MPREAL_MAJOR_VERSION "${CMAKE_MATCH_1}") 44 | string(REGEX MATCH "define[ \t]+MPREAL_VERSION_MINOR[ \t]+([0-9]+)" 45 | _mpreal_minor_version_match "${_mpreal_version_header}") 46 | set(MPREAL_MINOR_VERSION "${CMAKE_MATCH_1}") 47 | string(REGEX MATCH "define[ \t]+MPREAL_VERSION_PATCHLEVEL[ \t]+([0-9]+)" 48 | _mpreal_patchlevel_version_match "${_mpreal_version_header}") 49 | set(MPREAL_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") 50 | 51 | set(MPREAL_VERSION 52 | ${MPREAL_MAJOR_VERSION}.${MPREAL_MINOR_VERSION}.${MPREAL_PATCHLEVEL_VERSION}) 53 | 54 | # Check whether found version exceeds minimum required 55 | if(${MPREAL_VERSION} VERSION_LESS ${MPREAL_FIND_VERSION}) 56 | set(MPREAL_VERSION_OK FALSE) 57 | message(STATUS "MPFR C++ version ${MPREAL_VERSION} found in ${MPREAL_INCLUDES}, " 58 | "but at least version ${MPREAL_FIND_VERSION} is required") 59 | else() 60 | set(MPREAL_VERSION_OK TRUE) 61 | endif() 62 | endif() 63 | 64 | include(FindPackageHandleStandardArgs) 65 | find_package_handle_standard_args(MPREAL DEFAULT_MSG MPREAL_INCLUDES MPREAL_VERSION_OK) 66 | if (MPREAL_FOUND) 67 | message(STATUS "MPFR C++ version: ${MPREAL_VERSION}") 68 | endif (MPREAL_FOUND) 69 | mark_as_advanced(MPREAL_INCLUDES) 70 | 71 | -------------------------------------------------------------------------------- /cmake/Modules/FindMPFR.cmake: -------------------------------------------------------------------------------- 1 | # Try to find the MPFR library 2 | # See http://www.mpfr.org/ 3 | # 4 | # This module supports requiring a minimum version, e.g. you can do 5 | # find_package(MPFR 2.3.0) 6 | # to require version 2.3.0 to newer of MPFR. 7 | # 8 | # Once done this will define 9 | # 10 | # MPFR_FOUND - system has MPFR lib with correct version 11 | # MPFR_INCLUDES - the MPFR include directory 12 | # MPFR_LIBRARIES - the MPFR library 13 | # MPFR_VERSION - MPFR version 14 | 15 | # Copyright (c) 2006, 2007 Montel Laurent, 16 | # Copyright (c) 2008, 2009 Gael Guennebaud, 17 | # Copyright (c) 2010 Jitse Niesen, 18 | # Copyright (c) 2015 Jack Poulson, 19 | # Redistribution and use is allowed according to the terms of the BSD license. 20 | 21 | find_path(MPFR_INCLUDES NAMES mpfr.h PATHS $ENV{GMPDIR} $ENV{MPFRDIR} 22 | ${INCLUDE_INSTALL_DIR}) 23 | 24 | # Set MPFR_FIND_VERSION to 1.0.0 if no minimum version is specified 25 | if(NOT MPFR_FIND_VERSION) 26 | if(NOT MPFR_FIND_VERSION_MAJOR) 27 | set(MPFR_FIND_VERSION_MAJOR 1) 28 | endif() 29 | if(NOT MPFR_FIND_VERSION_MINOR) 30 | set(MPFR_FIND_VERSION_MINOR 0) 31 | endif() 32 | if(NOT MPFR_FIND_VERSION_PATCH) 33 | set(MPFR_FIND_VERSION_PATCH 0) 34 | endif() 35 | set(MPFR_FIND_VERSION 36 | "${MPFR_FIND_VERSION_MAJOR}.${MPFR_FIND_VERSION_MINOR}.${MPFR_FIND_VERSION_PATCH}") 37 | endif() 38 | 39 | if(MPFR_INCLUDES) 40 | # Query MPFR_VERSION 41 | file(READ "${MPFR_INCLUDES}/mpfr.h" _mpfr_version_header) 42 | 43 | string(REGEX MATCH "define[ \t]+MPFR_VERSION_MAJOR[ \t]+([0-9]+)" 44 | _mpfr_major_version_match "${_mpfr_version_header}") 45 | set(MPFR_MAJOR_VERSION "${CMAKE_MATCH_1}") 46 | string(REGEX MATCH "define[ \t]+MPFR_VERSION_MINOR[ \t]+([0-9]+)" 47 | _mpfr_minor_version_match "${_mpfr_version_header}") 48 | set(MPFR_MINOR_VERSION "${CMAKE_MATCH_1}") 49 | string(REGEX MATCH "define[ \t]+MPFR_VERSION_PATCHLEVEL[ \t]+([0-9]+)" 50 | _mpfr_patchlevel_version_match "${_mpfr_version_header}") 51 | set(MPFR_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") 52 | 53 | set(MPFR_VERSION 54 | ${MPFR_MAJOR_VERSION}.${MPFR_MINOR_VERSION}.${MPFR_PATCHLEVEL_VERSION}) 55 | 56 | # Check whether found version exceeds minimum required 57 | if(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION}) 58 | set(MPFR_VERSION_OK FALSE) 59 | message(STATUS "MPFR version ${MPFR_VERSION} found in ${MPFR_INCLUDES}, " 60 | "but at least version ${MPFR_FIND_VERSION} is required") 61 | else() 62 | set(MPFR_VERSION_OK TRUE) 63 | endif() 64 | endif() 65 | 66 | find_library(MPFR_LIBRARIES mpfr 67 | PATHS $ENV{GMPDIR} $ENV{MPFRDIR} ${LIB_INSTALL_DIR}) 68 | 69 | find_library(GMP_LIBRARIES gmp 70 | PATHS $ENV{GMPDIR} $ENV{MPFRDIR} ${LIB_INSTALL_DIR}) 71 | 72 | include(FindPackageHandleStandardArgs) 73 | find_package_handle_standard_args(MPFR DEFAULT_MSG MPFR_INCLUDES MPFR_LIBRARIES GMP_LIBRARIES MPFR_VERSION_OK) 74 | if (MPFR_FOUND) 75 | message(STATUS "MPFR version: ${MPFR_VERSION}") 76 | endif (MPFR_FOUND) 77 | set(MPFR_LIBRARIES ${MPFR_LIBRARIES} ${GMP_LIBRARIES}) 78 | mark_as_advanced(MPFR_INCLUDES MPFR_LIBRARIES) 79 | 80 | -------------------------------------------------------------------------------- /examples/bell_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2019, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """ 9 | Example use of the JKU Provider and the Qasm Simulator backend for creating a 10 | Bell state 11 | """ 12 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, execute 13 | from qiskit_jku_provider import JKUProvider 14 | 15 | JKU = JKUProvider() 16 | 17 | qubits_num = 2 18 | qr = QuantumRegister(qubits_num) 19 | cr = ClassicalRegister(qubits_num) 20 | 21 | qc = QuantumCircuit(qr, cr) 22 | qc.h(qr[0]) 23 | qc.cx(qr[0], qr[1]) 24 | qc.measure(qr, cr) 25 | 26 | jku_backend = JKU.get_backend('qasm_simulator') 27 | job = execute(qc, backend=jku_backend, shots=1000, seed=42) 28 | result = job.result() 29 | print(result.get_counts()) 30 | -------------------------------------------------------------------------------- /examples/jku_backends.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2019, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Usage examples for the JKU Provider""" 9 | 10 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, execute 11 | from qiskit_jku_provider import JKUProvider 12 | 13 | JKU = JKUProvider() 14 | 15 | jku_backend = JKU.get_backend('qasm_simulator') 16 | 17 | print(jku_backend) 18 | 19 | # gets the name of the backend. 20 | print(jku_backend.name()) 21 | 22 | # gets the status of the backend. 23 | print(jku_backend.status()) 24 | 25 | # returns the provider of the backend 26 | print(jku_backend.provider()) 27 | 28 | # gets the configuration of the backend. 29 | print(jku_backend.configuration()) 30 | 31 | # gets the properties of the backend. 32 | print(jku_backend.properties()) 33 | 34 | # Demonstration of Job 35 | qr = QuantumRegister(2) 36 | cr = ClassicalRegister(2) 37 | 38 | qc = QuantumCircuit(qr, cr) 39 | qc.h(qr[0]) 40 | qc.cx(qr[0], qr[1]) 41 | qc.measure(qr, cr) 42 | 43 | job = execute(qc, backend=jku_backend) 44 | 45 | # gets the backend the job was run on 46 | backend = job.backend() 47 | print(backend) 48 | 49 | # returns the status of the job. Should be running 50 | print(job.status()) 51 | 52 | # returns the obj that was run 53 | qobj = job.qobj() 54 | print(qobj) 55 | 56 | # prints the job id 57 | print(job.job_id()) 58 | 59 | # cancels the job 60 | print(job.cancel()) 61 | 62 | # returns the status of the job. Should be canceled 63 | print(job.status()) 64 | 65 | # runs the qjob on the backend 66 | job2 = backend.run(qobj) 67 | 68 | # gets the result of the job. This is a blocker 69 | print(job2.result()) 70 | -------------------------------------------------------------------------------- /examples/two_register.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2019, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """ 9 | Example use of the JKU Provider and the Qasm Simulator backend for creating the 10 | state '01 10'. 11 | """ 12 | from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit, execute 13 | from qiskit_jku_provider import JKUProvider 14 | 15 | JKU = JKUProvider() 16 | 17 | qreg0 = QuantumRegister(2, 'q0') 18 | creg0 = ClassicalRegister(2, 'c0') 19 | qreg1 = QuantumRegister(2, 'q1') 20 | creg1 = ClassicalRegister(2, 'c1') 21 | circ = QuantumCircuit(qreg0, qreg1) 22 | circ.x(qreg0[1]) 23 | circ.x(qreg1[0]) 24 | 25 | meas = QuantumCircuit(qreg0, qreg1, creg0, creg1) 26 | meas.measure(qreg0, creg0) 27 | meas.measure(qreg1, creg1) 28 | 29 | qc = circ + meas 30 | 31 | backend_sim = JKU.get_backend('qasm_simulator') 32 | job = execute(qc, backend_sim) 33 | result = job.result() 34 | counts = result.get_counts(qc) 35 | print(counts) 36 | -------------------------------------------------------------------------------- /jku_tcad.bib: -------------------------------------------------------------------------------- 1 | @article{zulehner2018advanced, 2 | title={Advanced simulation of quantum computations}, 3 | author={Zulehner, Alwin and Wille, Robert}, 4 | journal={IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems}, 5 | year={2018}, 6 | publisher={IEEE} 7 | } 8 | -------------------------------------------------------------------------------- /qiskit_jku_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Local JKU Backend.""" 9 | 10 | from .qasm_simulator_jku import QasmSimulator 11 | from .jkuprovider import JKUProvider 12 | -------------------------------------------------------------------------------- /qiskit_jku_provider/jkujob.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """This module implements the job class used for QasmSimulatorJKU objects.""" 9 | 10 | import functools 11 | import logging 12 | import sys 13 | from concurrent import futures 14 | 15 | from qiskit.providers import BaseJob, JobError, JobStatus 16 | from qiskit.qobj import validate_qobj_against_schema 17 | 18 | logger = logging.getLogger(__name__) 19 | 20 | 21 | def requires_submit(func): 22 | """ 23 | Decorator to ensure that a submit has been performed before 24 | calling the method. 25 | 26 | Args: 27 | func (callable): test function to be decorated. 28 | 29 | Returns: 30 | callable: the decorated function. 31 | """ 32 | @functools.wraps(func) 33 | def _wrapper(self, *args, **kwargs): 34 | if self._future is None: 35 | raise JobError("Job not submitted yet!. You have to .submit() first!") 36 | return func(self, *args, **kwargs) 37 | return _wrapper 38 | 39 | 40 | class JKUJob(BaseJob): 41 | """JKU Job class. 42 | 43 | Attributes: 44 | _executor (futures.Executor): executor to handle asynchronous jobs 45 | """ 46 | 47 | if sys.platform in ['darwin', 'win32']: 48 | _executor = futures.ThreadPoolExecutor() 49 | else: 50 | _executor = futures.ProcessPoolExecutor() 51 | 52 | def __init__(self, backend, job_id, fn, qobj): 53 | super().__init__(backend, job_id) 54 | self._fn = fn 55 | self._qobj = qobj 56 | self._future = None 57 | 58 | def submit(self): 59 | """Submit the job to the backend for execution. 60 | 61 | Raises: 62 | QobjValidationError: if the JSON serialization of the Qobj passed 63 | during construction does not validate against the Qobj schema. 64 | 65 | JobError: if trying to re-submit the job. 66 | """ 67 | if self._future is not None: 68 | raise JobError("We have already submitted the job!") 69 | 70 | validate_qobj_against_schema(self._qobj) 71 | self._future = self._executor.submit(self._fn, self._job_id, self._qobj) 72 | 73 | @requires_submit 74 | def result(self, timeout=None): 75 | # pylint: disable=arguments-differ 76 | """Get job result. The behavior is the same as the underlying 77 | concurrent Future objects, 78 | 79 | https://docs.python.org/3/library/concurrent.futures.html#future-objects 80 | 81 | Args: 82 | timeout (float): number of seconds to wait for results. 83 | 84 | Returns: 85 | qiskit.Result: Result object 86 | 87 | Raises: 88 | concurrent.futures.TimeoutError: if timeout occurred. 89 | concurrent.futures.CancelledError: if job cancelled before completed. 90 | """ 91 | return self._future.result(timeout=timeout) 92 | 93 | @requires_submit 94 | def cancel(self): 95 | return self._future.cancel() 96 | 97 | @requires_submit 98 | def status(self): 99 | """Gets the status of the job by querying the Python's future 100 | 101 | Returns: 102 | JobStatus: The current JobStatus 103 | 104 | Raises: 105 | JobError: If the future is in unexpected state 106 | concurrent.futures.TimeoutError: if timeout occurred. 107 | """ 108 | # The order is important here 109 | if self._future.running(): 110 | _status = JobStatus.RUNNING 111 | elif self._future.cancelled(): 112 | _status = JobStatus.CANCELLED 113 | elif self._future.done(): 114 | _status = JobStatus.DONE if self._future.exception() is None else JobStatus.ERROR 115 | elif self._future._state == 'PENDING': 116 | _status = JobStatus.QUEUED 117 | else: 118 | raise JobError('Unexpected behavior of {0}'.format( 119 | self.__class__.__name__)) 120 | return _status 121 | 122 | def backend(self): 123 | """Return the instance of the backend used for this job.""" 124 | return self._backend 125 | 126 | def qobj(self): 127 | """Return the Qobj submitted for this job. 128 | Returns: 129 | Qobj: the Qobj submitted for this job. 130 | """ 131 | return self._qobj 132 | -------------------------------------------------------------------------------- /qiskit_jku_provider/jkuprovider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Provider for the local JKU backend.""" 9 | 10 | import logging 11 | 12 | from qiskit.providers import BaseProvider 13 | from .qasm_simulator_jku import QasmSimulator 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | class JKUProvider(BaseProvider): 19 | """Provider for the local JKU backend.""" 20 | 21 | def __init__(self, *args, **kwargs): 22 | super().__init__(args, kwargs) 23 | 24 | # Populate the list of local JKU backends. 25 | self.backends_list = {'qasm_simulator': QasmSimulator(provider=self)} 26 | 27 | def get_backend(self, name, **kwargs): 28 | return self.backends_list[name] 29 | 30 | def available_backends(self, filters=None): 31 | # pylint: disable=arguments-differ 32 | backends = self.backends_list 33 | 34 | filters = filters or {} 35 | for key, value in filters.items(): 36 | backends = {name: instance for name, instance in backends.items() 37 | if instance.configuration().get(key) == value} 38 | 39 | return list(backends.values()) 40 | 41 | def backends(self, name=None, **kwargs): 42 | return list(self.backends_list.values()) 43 | 44 | def __str__(self): 45 | return 'JKUProvider' 46 | -------------------------------------------------------------------------------- /qiskit_jku_provider/jkusimulatorerror.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """ 9 | Exception for errors raised by JKU simulator. 10 | """ 11 | 12 | 13 | from qiskit import QiskitError 14 | 15 | 16 | class JKUSimulatorError(QiskitError): 17 | """Class for errors raised by the JKU simulator.""" 18 | 19 | def __init__(self, *message): 20 | """Set the error message.""" 21 | super().__init__(*message) 22 | self.message = ' '.join(message) 23 | 24 | def __str__(self): 25 | """Return the message.""" 26 | return repr(self.message) 27 | -------------------------------------------------------------------------------- /qiskit_jku_provider/qasm_simulator_jku.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Backend for the JKU C++ simulator.""" 9 | 10 | import itertools 11 | import json 12 | import logging 13 | import operator 14 | import os 15 | import platform 16 | import random 17 | import subprocess 18 | import time 19 | import uuid 20 | 21 | from qiskit.providers import BaseBackend 22 | from qiskit.providers.models import BackendConfiguration, BackendStatus 23 | from qiskit.result import Result 24 | 25 | from .jkujob import JKUJob 26 | from .jkusimulatorerror import JKUSimulatorError 27 | 28 | RUN_MESSAGE = """DD-based simulator by JKU Linz, Austria 29 | Developer: Alwin Zulehner, Robert Wille 30 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation""" 31 | 32 | qelib1 = """gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; } 33 | gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; } 34 | gate u1(lambda) q { U(0,0,lambda) q; } 35 | gate cx c,t { CX c,t; } 36 | gate id a { U(0,0,0) a; } 37 | gate u0(gamma) q { U(0,0,0) q; } 38 | gate x a { u3(pi,0,pi) a; } 39 | gate y a { u3(pi,pi/2,pi/2) a; } 40 | gate z a { u1(pi) a; } 41 | gate h a { u2(0,pi) a; } 42 | gate s a { u1(pi/2) a; } 43 | gate sdg a { u1(-pi/2) a; } 44 | gate t a { u1(pi/4) a; } 45 | gate tdg a { u1(-pi/4) a; } 46 | gate rx(theta) a { u3(theta, -pi/2,pi/2) a; } 47 | gate ry(theta) a { u3(theta,0,0) a; } 48 | gate rz(phi) a { u1(phi) a; } 49 | gate cz a,b { h b; cx a,b; h b; } 50 | gate cy a,b { sdg b; cx a,b; s b; } 51 | gate swap a,b { cx a,b; cx b,a; cx a,b; } 52 | gate ch a,b {h b; sdg b; cx a,b; h b; t b; cx a,b; t b; h b; s b; x b; s a;} 53 | gate ccx a,b,c {h c; cx b,c; tdg c; cx a,c; t c; cx b,c; tdg c; cx a,c; t b; t c; h c; cx a,b; \ 54 | t a; tdg b; cx a,b;} 55 | gate cswap a,b,c {cx c,b; ccx a,b,c; cx c,b;} 56 | gate crz(lambda) a,b {u1(lambda/2) b; cx a,b; u1(-lambda/2) b; cx a,b;} 57 | gate cu1(lambda) a,b {u1(lambda/2) a; cx a,b; u1(-lambda/2) b; cx a,b; u1(lambda/2) b;} 58 | gate cu3(theta,phi,lambda) c,t {u1((lambda-phi)/2) t; cx c,t; u3(-theta/2,0,-(phi+lambda)/2) t; \ 59 | cx c,t; u3(theta/2,phi,0) t;} 60 | gate rzz(theta) a,b {cx a,b; u1(theta) b; cx a,b;}\n""" 61 | 62 | 63 | # this class handles the actual technical details of converting to and from QISKit style data 64 | class JKUSimulatorWrapper: 65 | """Converter to and from JKU's simulator""" 66 | 67 | def __init__(self, exe=None, silent=False): 68 | self.seed = 0 69 | self.shots = 1 70 | self.exec = exe 71 | self.additional_output_data = [] 72 | self.silent = silent 73 | 74 | def set_config(self, global_config, experiment_config): 75 | if hasattr(experiment_config, 'data'): # additional output data specifications 76 | self.additional_output_data = experiment_config.data 77 | if hasattr(global_config, 'seed'): 78 | self.seed = global_config.seed 79 | else: 80 | self.seed = random.getrandbits(32) 81 | 82 | def run(self, qasm): 83 | """performs the actual external call to the JKU exe""" 84 | 85 | cmd = [self.exec, 86 | '--simulate_qasm', 87 | '--seed={}'.format(self.seed), 88 | '--shots={}'.format(self.shots), 89 | '--display_statevector', 90 | ] 91 | if 'probabilities' in self.additional_output_data: 92 | cmd.append('--display_probabilities') 93 | if not self.silent: 94 | print(RUN_MESSAGE) 95 | output = subprocess.check_output(cmd, input=qasm, stderr=subprocess.STDOUT, 96 | universal_newlines=True) 97 | return output 98 | 99 | def convert_operation_to_line(self, op, qubit_names, clbit_names): 100 | """convert one operation from the qobj file to a QASM line in the format JKU can handle""" 101 | name_string = op.name 102 | qubits_string = ", ".join([qubit_names[i] for i in op.qubits]) 103 | if hasattr(op, 'params') and len(op.params) > 0: 104 | params_string = "({})".format(", ".join([str(p) for p in op.params])) 105 | else: 106 | params_string = "" 107 | if name_string == "measure": # special syntax 108 | return "measure {} -> {};".format(qubit_names[op.qubits[0]], clbit_names[op.memory[0]]) 109 | # some QISKit bug causes snapshot params to be passed as floats 110 | if name_string == "snapshot": 111 | params_string = "({})".format(", ".join([str(int(p)) for p in op.params])) 112 | for p in op.params: 113 | self.max_snapshot_index = max(self.max_snapshot_index, int(p)) 114 | return "{}{} {};".format(name_string, params_string, qubits_string) 115 | 116 | def add_final_snapshot(self, qasm_file_lines, qubit_names): 117 | """Adds a final snapshot at the end of the file, e.g. to get the final statevector""" 118 | self.max_snapshot_index = self.max_snapshot_index + 1 119 | qubits = ", ".join(qubit_names) 120 | qasm_file_lines.append("snapshot({}) {};".format(self.max_snapshot_index, qubits)) 121 | 122 | # converts the full qobj circuit (except measurements) to a QASM file JKU can handle 123 | def convert_qobj_circuit_to_jku_qasm(self, experiment): 124 | instructions = experiment.instructions 125 | qubit_num = len(experiment.header.qubit_labels) 126 | clbit_num = len(experiment.header.clbit_labels) 127 | # arbitrary qubit names, to use only in the temp qasm file we pass to JKU's simulator 128 | qubit_names = ['q[{}]'.format(i) for i in range(qubit_num)] 129 | clbit_names = ['c[{}]'.format(i) for i in range(clbit_num)] 130 | qasm_file_lines = ["OPENQASM 2.0;", 131 | "include \"qelib1.inc\";", 132 | "qreg q[{}];".format(qubit_num), 133 | "creg c[{}];".format(qubit_num) 134 | ] 135 | self.max_snapshot_index = 0 136 | for op in instructions: 137 | qasm_file_lines.append(self.convert_operation_to_line(op, qubit_names, clbit_names)) 138 | self.add_final_snapshot(qasm_file_lines, qubit_names) 139 | qasm_content = "\n".join(qasm_file_lines) + "\n" 140 | return qasm_content 141 | 142 | def final_statevector(self, run_output): 143 | """ 144 | Retrieves the statevector at the end of the computation 145 | """ 146 | return run_output["snapshots"][str(self.max_snapshot_index)]["statevector"] 147 | 148 | # runs the qobj circuit on the JKU exe while performing input/output conversions 149 | def run_experiment(self, config, experiment): 150 | self.set_config(config, experiment.config) 151 | # do this before running so we can output warning to the user as soon as possible if needed 152 | measurement_data = self.compute_measurement_data(experiment) 153 | with open("qelib1.inc", "w") as qelib_file: 154 | qelib_file.write(qelib1) 155 | self.start_time = time.time() 156 | qasm = self.convert_qobj_circuit_to_jku_qasm(experiment) 157 | run_output = self.run(qasm) 158 | self.end_time = time.time() 159 | if os.path.exists("qelib1.inc"): 160 | os.remove("qelib1.inc") 161 | output_data = self.parse_output(run_output, measurement_data) 162 | result_dict = {'header': {'name': experiment.header.name, 163 | 'memory_slots': experiment.config.memory_slots, 164 | 'creg_sizes': experiment.header.creg_sizes 165 | }, 166 | 'status': 'DONE', 'time_taken': self.end_time - self.start_time, 167 | 'seed': self.seed, 'shots': self.shots, 168 | 'data': output_data, 169 | 'success': True 170 | } 171 | return result_dict 172 | 173 | # parsing the textual JKU output 174 | def parse_output(self, run_output, measurement_data): 175 | result = json.loads(run_output) 176 | qubits = measurement_data['qubits_num'] 177 | # QISKit qubit order is reversed, so we fix accordingly 178 | translation_table = [0] * 2 ** qubits 179 | for n in range(2 ** qubits): 180 | translation_table[n] = int(bin(n)[2:].rjust(qubits, '0')[::-1], 2) 181 | if 'counts' in result: 182 | result['counts'] = self.convert_counts(result['counts'], measurement_data) 183 | if 'snapshots' in result: 184 | for snapshot_key, snapshot_data in result['snapshots'].items(): 185 | result['snapshots'][snapshot_key] = self.convert_snapshot(snapshot_data, 186 | translation_table) 187 | result['statevector'] = self.final_statevector(result) 188 | return result 189 | 190 | def convert_snapshot(self, snapshot_data, translation_table): 191 | if 'statevector' in snapshot_data: 192 | snapshot_data['statevector'] = self.convert_statevector_data( 193 | snapshot_data['statevector'], translation_table) 194 | if 'probabilities' in snapshot_data: 195 | probs_data = snapshot_data.pop('probabilities') 196 | if 'probabilities' in self.additional_output_data: 197 | snapshot_data['probabilities'] = self.convert_probabilities(probs_data, 198 | translation_table) 199 | if 'probabilities_ket' in snapshot_data: 200 | probs_ket_data = snapshot_data.pop('probabilities_ket') 201 | if 'probabilities_ket' in self.additional_output_data: 202 | snapshot_data['probabilities_ket'] = self.convert_probabilities_ket(probs_ket_data) 203 | return snapshot_data 204 | 205 | def to_qiskit_complex(self, num_string): 206 | num = complex(num_string.replace('i', 'j')) # first obtain an actual number 207 | return [num.real, num.imag] 208 | 209 | def convert_statevector_data(self, statevector, translation_table): 210 | return [self.to_qiskit_complex(statevector[translation_table[i]]) 211 | for i in range(len(translation_table))] 212 | 213 | def convert_probabilities(self, probs_data, translation_table): 214 | return [probs_data[translation_table[i]] for i in range(len(translation_table))] 215 | 216 | def convert_probabilities_ket(self, probs_ket_data): 217 | return dict([(key[::-1], value) for key, value in probs_ket_data.items()]) 218 | 219 | def convert_counts(self, counts, measurement_data): 220 | result = {} 221 | for qubits, count in counts.items(): 222 | clbits = self.qubits_to_clbits(qubits, measurement_data) 223 | if clbits is not None: 224 | result[clbits] = result.get(clbits, 0) + count 225 | return result 226 | 227 | # converting the actual measurement results for all qubits to clbits the user expects to see 228 | def qubits_to_clbits(self, qubits, measurement_data): 229 | clbits = list('0' * measurement_data['clbits_num']) 230 | for (qubit, clbit) in measurement_data['mapping'].items(): 231 | clbits[clbit] = qubits[qubit] 232 | s = "".join(clbits)[::-1] 233 | if s == '': 234 | return None 235 | return hex(int(s, 2)) 236 | 237 | # finding the data relevant to measurements and clbits in the qobj_circuit 238 | def compute_measurement_data(self, experiment): 239 | # Ignore (and inform the user) any in-circuit measurements 240 | # Create a mapping of qubit --> classical bit for any end-circuit measurement 241 | header = experiment.header 242 | measurement_data = {'mapping': {}, 243 | 'clbits': header.creg_sizes, 244 | 'clbits_num': len(header.clbit_labels), 245 | 'qubits_num': len(header.qubit_labels)} 246 | ops = experiment.instructions 247 | for op in ops: 248 | if op.name == "measure": 249 | measurement_data['mapping'][op.qubits[0]] = op.memory[0] 250 | else: 251 | for qubit in op.qubits: 252 | if qubit in measurement_data['mapping'].keys() and not op.name == 'snapshot': 253 | raise JKUSimulatorError( 254 | "Error: qubit {} was used after being measured. This is currently " 255 | "not supported by JKU".format(qubit)) 256 | return measurement_data 257 | 258 | 259 | logger = logging.getLogger(__name__) 260 | 261 | EXTENSION = '.exe' if platform.system() == 'Windows' else '' 262 | 263 | # Add path to compiled qasm simulator 264 | DEFAULT_SIMULATOR_PATHS = [ 265 | # This is the path where Makefile creates the simulator by default 266 | os.path.abspath(os.path.join(os.path.dirname(__file__), 267 | '../build/lib/qiskit_jku_provider/jku_simulator' 268 | + EXTENSION)), 269 | # This is the path where PIP installs the simulator 270 | os.path.abspath(os.path.join(os.path.dirname(__file__), 271 | 'jku_simulator' + EXTENSION)), 272 | ] 273 | 274 | VERSION_PATHS = [ 275 | os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "VERSION.txt")), 276 | os.path.abspath(os.path.join(os.path.dirname(__file__), "VERSION.txt")), 277 | ] 278 | 279 | for version_path in VERSION_PATHS: 280 | if os.path.exists(version_path): 281 | with open(version_path, "r") as version_file: 282 | VERSION = version_file.read().strip() 283 | 284 | 285 | class QasmSimulator(BaseBackend): 286 | """Python interface to JKU's simulator""" 287 | 288 | DEFAULT_CONFIGURATION = { 289 | 'backend_name': 'qasm_simulator', 290 | 'backend_version': VERSION, 291 | 'url': 'https://github.com/Qiskit/qiskit-jku-provider', 292 | 'simulator': True, 293 | 'local': True, 294 | 'description': 'JKU C++ simulator', 295 | 'basis_gates': ['u0', 'u1', 'u2', 'u3', 'cx', 'x', 'y', 'z', 'h', 's', 't', 'snapshot'], 296 | 'memory': False, 297 | 'n_qubits': 30, 298 | 'conditional': False, 299 | 'max_shots': 100000, 300 | 'coupling_map': None, 301 | 'open_pulse': False, 302 | 'gates': [ 303 | { 304 | 'name': 'TODO', 305 | 'parameters': [], 306 | 'qasm_def': 'TODO' 307 | } 308 | ] 309 | } 310 | 311 | def __init__(self, configuration=None, provider=None, silent=False): 312 | """ 313 | Args: 314 | configuration (dict): backend configuration 315 | Raises: 316 | ImportError: if the JKU simulator is not available. 317 | """ 318 | super().__init__(configuration=(configuration or 319 | BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), 320 | provider=provider) 321 | 322 | paths = DEFAULT_SIMULATOR_PATHS 323 | # Ensure that the executable is available. 324 | try: 325 | self.executable = next( 326 | path for path in paths if (os.path.exists(path) and 327 | os.path.getsize(path) > 100)) 328 | except StopIteration: 329 | print(paths) 330 | raise FileNotFoundError('Simulator executable not found') 331 | 332 | self.silent = silent 333 | 334 | def run(self, qobj): 335 | job_id = str(uuid.uuid4()) 336 | local_job = JKUJob(self, job_id, self._run_job, qobj) 337 | local_job.submit() 338 | return local_job 339 | 340 | def _run_job(self, job_id, qobj): 341 | """Run circuits in q_job""" 342 | result_list = [] 343 | self._validate(qobj) 344 | 345 | s = JKUSimulatorWrapper(self.executable, silent=self.silent) 346 | s.shots = qobj.config.shots 347 | start = time.time() 348 | for experiment in qobj.experiments: 349 | result_list.append(s.run_experiment(qobj.config, experiment)) 350 | end = time.time() 351 | result = {'backend_name': self._configuration.backend_name, 352 | 'backend_version': VERSION, 353 | 'qobj_id': qobj.qobj_id, 354 | 'job_id': job_id, 355 | 'results': result_list, 356 | 'status': 'COMPLETED', 357 | 'success': True, 358 | 'time_taken': (end - start)} 359 | return Result.from_dict(result) 360 | 361 | def _validate(self, qobj): 362 | # for now, JKU should be ran with shots = 1 and no measurement gates 363 | # hence, we do not check for those cases as in the default qasm simulator 364 | return 365 | 366 | def status(self): 367 | """Return backend status. 368 | 369 | Returns: 370 | BackendStatus: the status of the backend. 371 | """ 372 | return BackendStatus(backend_name=self.name(), 373 | backend_version=self.configuration().backend_version, 374 | operational=True, 375 | pending_jobs=0, 376 | status_msg='') 377 | 378 | 379 | def _get_register_specs(bit_labels): 380 | """ 381 | Get the number and size of unique registers from bit_labels list with an 382 | iterator of register_name:size pairs. 383 | 384 | Args: 385 | bit_labels (list): this list is of the form:: 386 | 387 | [['reg1', 0], ['reg1', 1], ['reg2', 0]] 388 | 389 | which indicates a register named "reg1" of size 2 390 | and a register named "reg2" of size 1. This is the 391 | format of classic and quantum bit labels in qobj 392 | header. 393 | Yields: 394 | tuple: pairs of (register_name, size) 395 | """ 396 | iterator = itertools.groupby(bit_labels, operator.itemgetter(0)) 397 | for register_name, sub_it in iterator: 398 | yield register_name, max(ind[1] for ind in sub_it) + 1 399 | 400 | 401 | def _format_result(counts, cl_reg_index, cl_reg_nbits): 402 | """Format the result bit string. 403 | 404 | This formats the result bit strings such that spaces are inserted 405 | at register divisions. 406 | 407 | Args: 408 | counts (dict): dictionary of counts e.g. {'1111': 1000, '0000':5} 409 | cl_reg_index (list): starting bit index of classical register 410 | cl_reg_nbits (list): total amount of bits in classical register 411 | Returns: 412 | dict: spaces inserted into dictionary keys at register boundaries. 413 | """ 414 | fcounts = {} 415 | for key, value in counts.items(): 416 | new_key = [key[-cl_reg_nbits[0]:]] 417 | for index, nbits in zip(cl_reg_index[1:], 418 | cl_reg_nbits[1:]): 419 | new_key.insert(0, key[-(index + nbits):-index]) 420 | fcounts[' '.join(new_key)] = value 421 | return fcounts 422 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pycodestyle 2 | pylint>=2.2,<2.3 3 | vcrpy -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | 9 | import os 10 | import platform 11 | from distutils.command.build import build 12 | from subprocess import call 13 | 14 | from setuptools.dist import Distribution 15 | from setuptools import setup, find_packages 16 | 17 | VERSION_PATH = os.path.join(os.path.dirname(__file__), "VERSION.txt") 18 | with open(VERSION_PATH, "r") as version_file: 19 | VERSION = version_file.read().strip() 20 | 21 | class JKUSimulatorBuild(build): 22 | def run(self): 23 | super().run() 24 | # Store the current working directory, as invoking cmake involves 25 | # an out of source build and might interfere with the rest of the steps. 26 | current_directory = os.getcwd() 27 | BUILD_DIR = "build/lib/qiskit_jku_provider" 28 | try: 29 | supported_platforms = ['Linux', 'Darwin', 'Windows'] 30 | current_platform = platform.system() 31 | if current_platform not in supported_platforms: 32 | # TODO: stdout is silenced by pip if the full setup.py invocation is 33 | # successful, unless using '-v' - hence the warnings are not printed. 34 | print('WARNING: JKU simulator is meant to be built with these ' 35 | 'platforms: {}. We will support other platforms soon!' 36 | .format(supported_platforms)) 37 | return 38 | 39 | cmd_cmake = ['cmake', '-vvv', '-DSTATIC_LINKING=True'] 40 | if 'USER_LIB_PATH' in os.environ: 41 | cmd_cmake.append('-DUSER_LIB_PATH={}'.format(os.environ['USER_LIB_PATH'])) 42 | if current_platform == 'Windows': 43 | # We only support MinGW so far 44 | cmd_cmake.append("-GMinGW Makefiles") 45 | cmd_cmake.append('.') 46 | cmd_cmake.append('-B{}'.format(BUILD_DIR)) 47 | 48 | make_exe = 'make' 49 | if current_platform == 'Windows': 50 | make_exe = 'mingw32-make' 51 | 52 | cmd_make = [make_exe, '-C', BUILD_DIR] 53 | 54 | def compile_simulator(): 55 | print(" ".join(cmd_cmake)) 56 | call(cmd_cmake) 57 | call(cmd_make) 58 | if current_platform == 'Windows': 59 | copy_dll_files() #in case we dont have static versions of MPFR and MPIR for windows 60 | 61 | def copy_dll_files(): 62 | try: 63 | from shutil import copyfile 64 | build_dir = os.path.abspath(BUILD_DIR) 65 | print("-- Checking for enviornment variable MPFRDIR... {}".format("MPFRDIR" in os.environ)) 66 | mpfr_dll = os.path.join(os.environ["MPFRDIR"], "mpfr.dll") 67 | mpfr_dll_dst = os.path.join(build_dir, "mpfr.dll") 68 | print("-- Checking for enviornment variable GMPDIR... {}".format("GMPDIR" in os.environ)) 69 | mpir_dll = os.path.join(os.environ["GMPDIR"], "mpir.dll") 70 | mpir_dll_dst = os.path.join(build_dir, "mpir.dll") 71 | print("-- Copying {} to {}".format(mpfr_dll, mpfr_dll_dst)) 72 | copyfile(mpfr_dll, mpfr_dll_dst) 73 | print("-- Copying {} to {}".format(mpir_dll, mpir_dll_dst)) 74 | copyfile(mpir_dll, mpir_dll_dst) 75 | 76 | except Exception as copy_exception: 77 | print("WARNING: DLL files copy failed: {}".format(copy_exception)) 78 | 79 | 80 | self.execute(compile_simulator, [], 'Compiling JKU Simulator') 81 | except Exception as e: 82 | print(str(e)) 83 | print("WARNING: Seems like the JKU simulator can't be built, Qiskit will " 84 | "install anyway, but won't have this simulator support.") 85 | 86 | # Restore working directory. 87 | os.chdir(current_directory) 88 | 89 | # This is for creating wheel specific platforms 90 | class BinaryDistribution(Distribution): 91 | def has_ext_modules(self): 92 | return True 93 | 94 | setup( 95 | name="qiskit-jku-provider", 96 | version=VERSION, 97 | author="Qiskit Development Team", 98 | author_email="qiskit@us.ibm.com", 99 | description="Qiskit simulator whose backend is based on JKU's simulator", 100 | long_description = "This module contains [Qiskit](https://www.qiskit.org/) simulator whose backend is written in JKU's simulator. This simulator simulate a Quantum circuit on a classical computer.", 101 | url="https://github.com/Qiskit/qiskit-jku-provider", 102 | license="Apache 2.0", 103 | classifiers=[ 104 | "Environment :: Console", 105 | "License :: OSI Approved :: Apache Software License", 106 | "Intended Audience :: Developers", 107 | "Intended Audience :: Science/Research", 108 | "Operating System :: Microsoft :: Windows", 109 | "Operating System :: MacOS", 110 | "Operating System :: POSIX :: Linux", 111 | "Programming Language :: Python :: 3.5", 112 | "Programming Language :: Python :: 3.6", 113 | "Topic :: Scientific/Engineering", 114 | ], 115 | install_requires=['qiskit-terra>=0.8'], 116 | keywords="qiskit quantum jku_simulator", 117 | packages=find_packages(exclude=['test*']), 118 | include_package_data=True, 119 | cmdclass={ 120 | 'build': JKUSimulatorBuild, 121 | }, 122 | distclass=BinaryDistribution 123 | 124 | ) 125 | -------------------------------------------------------------------------------- /src/QASMscanner.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | QASMscanner::QASMscanner(std::istream& in_stream) : in(in_stream) { 40 | // initialize error handling support 41 | keywords["qreg"] = Token::Kind::qreg; 42 | keywords["creg"] = Token::Kind::creg; 43 | keywords["gate"] = Token::Kind::gate; 44 | keywords["measure"] = Token::Kind::measure; 45 | keywords["U"] = Token::Kind::ugate; 46 | keywords["CX"] = Token::Kind::cxgate; 47 | keywords["pi"] = Token::Kind::pi; 48 | keywords["OPENQASM"] = Token::Kind::openqasm; 49 | keywords["show_probabilities"] = Token::Kind::probabilities; 50 | keywords["sin"] = Token::Kind::sin; 51 | keywords["cos"] = Token::Kind::cos; 52 | keywords["tan"] = Token::Kind::tan; 53 | keywords["exp"] = Token::Kind::exp; 54 | keywords["ln"] = Token::Kind::ln; 55 | keywords["sqrt"] = Token::Kind::sqrt; 56 | keywords["include"] = Token::Kind::include; 57 | keywords["barrier"] = Token::Kind::barrier; 58 | keywords["opaque"] = Token::Kind::opaque; 59 | keywords["if"] = Token::Kind::_if; 60 | keywords["reset"] = Token::Kind::reset; 61 | keywords["snapshot"] = Token::Kind::snapshot; 62 | line = 1; 63 | col = 0; 64 | ch = 0; 65 | nextCh(); 66 | } 67 | 68 | void QASMscanner::addFileInput(std::string fname) { 69 | std::ifstream* in = new std::ifstream (fname, std::ifstream::in); 70 | if(in->fail()) { 71 | std::cerr << "Failed to open file '" << fname << "'!" << std::endl; 72 | } else { 73 | streams.push(in); 74 | lines.push(LineInfo(ch, line, col)); 75 | line = 0; 76 | col = 0; 77 | } 78 | nextCh(); 79 | } 80 | 81 | void QASMscanner::nextCh() { 82 | if(!streams.empty() && streams.top()->eof()) { 83 | delete streams.top(); 84 | streams.pop(); 85 | ch = lines.top().ch; 86 | col = lines.top().col; 87 | line = lines.top().line; 88 | lines.pop(); 89 | return; 90 | } 91 | if(!streams.empty()) { 92 | col++; 93 | streams.top()->get(ch); 94 | } else { 95 | if(!in.eof()) { 96 | col++; 97 | in.get(ch); 98 | } else { 99 | ch = (char) -1; 100 | } 101 | } 102 | if(ch == '\n') { 103 | col = 0; 104 | line++; 105 | } 106 | } 107 | 108 | Token QASMscanner::next() { 109 | while(iswspace(ch)) { 110 | nextCh(); 111 | } 112 | 113 | Token t = Token(Token::Kind::none, line, col); 114 | 115 | switch(ch) { 116 | case 'a': 117 | case 'b': 118 | case 'c': 119 | case 'd': 120 | case 'e': 121 | case 'f': 122 | case 'g': 123 | case 'h': 124 | case 'i': 125 | case 'j': 126 | case 'k': 127 | case 'l': 128 | case 'm': 129 | case 'n': 130 | case 'o': 131 | case 'p': 132 | case 'q': 133 | case 'r': 134 | case 's': 135 | case 't': 136 | case 'u': 137 | case 'v': 138 | case 'w': 139 | case 'x': 140 | case 'y': 141 | case 'z': 142 | case 'A': 143 | case 'B': 144 | case 'C': 145 | case 'D': 146 | case 'E': 147 | case 'F': 148 | case 'G': 149 | case 'H': 150 | case 'I': 151 | case 'J': 152 | case 'K': 153 | case 'L': 154 | case 'M': 155 | case 'N': 156 | case 'O': 157 | case 'P': 158 | case 'Q': 159 | case 'R': 160 | case 'S': 161 | case 'T': 162 | case 'U': 163 | case 'V': 164 | case 'W': 165 | case 'X': 166 | case 'Y': 167 | case 'Z': 168 | readName(t);break; 169 | case '0': 170 | case '1': 171 | case '2': 172 | case '3': 173 | case '4': 174 | case '5': 175 | case '6': 176 | case '7': 177 | case '8': 178 | case '9': 179 | case '.': 180 | readNumber(t);break; 181 | case ';': t.kind = Token::Kind::semicolon; nextCh(); break; 182 | case (char) -1: t.kind = Token::Kind::eof; break; 183 | case '(': t.kind = Token::Kind::lpar; nextCh(); break; 184 | case ')': t.kind = Token::Kind::rpar; nextCh(); break; 185 | case '[': t.kind = Token::Kind::lbrack; nextCh(); break; 186 | case ']': t.kind = Token::Kind::rbrack; nextCh(); break; 187 | case '{': t.kind = Token::Kind::lbrace; nextCh(); break; 188 | case '}': t.kind = Token::Kind::rbrace; nextCh(); break; 189 | case ',': t.kind = Token::Kind::comma; nextCh(); break; 190 | case '+': nextCh(); t.kind = Token::Kind::plus; break; 191 | case '-': nextCh(); t.kind = Token::Kind::minus; break; 192 | case '*': nextCh(); t.kind = Token::Kind::times; break; 193 | case '/': nextCh(); if(ch == '/') { 194 | skipComment(); 195 | t = next(); 196 | } else { 197 | t.kind = Token::Kind::div; 198 | } 199 | break; 200 | case '^': nextCh(); t.kind = Token::Kind::power; break; 201 | case '"': nextCh(); readString(t); nextCh(); break; 202 | case '>': nextCh(); t.kind = Token::Kind::gt; break; 203 | case '=': nextCh(); if(ch == '=') { 204 | nextCh(); 205 | t.kind = Token::Kind::eq; 206 | } else { 207 | std::cerr << "ERROR: UNEXPECTED CHARACTER: '" << ch << "'! " << std::endl; 208 | } 209 | break; 210 | default: 211 | std::cerr << "ERROR: UNEXPECTED CHARACTER: '" << ch << "'! " << std::endl; 212 | nextCh(); 213 | } 214 | 215 | return t; 216 | } 217 | 218 | void QASMscanner::readString(Token& t) { 219 | std::stringstream ss; 220 | while(ch != '"') { 221 | ss << ch; 222 | nextCh(); 223 | } 224 | t.str = ss.str(); 225 | t.kind = Token::Kind::string; 226 | } 227 | 228 | void QASMscanner::skipComment() { 229 | while(ch != '\n' && ch != (char) -1) { 230 | nextCh(); 231 | } 232 | } 233 | 234 | void QASMscanner::readName(Token& t) { 235 | std::stringstream ss; 236 | while(isdigit(ch) || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') { 237 | ss << ch; 238 | nextCh(); 239 | } 240 | t.str = ss.str(); 241 | std::map::iterator it = keywords.find(t.str); 242 | if(it != keywords.end()) { 243 | t.kind = it->second; 244 | } else { 245 | t.kind = Token::Kind::identifier; 246 | } 247 | } 248 | 249 | void QASMscanner::readNumber(Token& t) { 250 | std::stringstream ss; 251 | while(isdigit(ch)) { 252 | ss << ch; 253 | nextCh(); 254 | } 255 | t.kind = Token::Kind::nninteger; 256 | t.str = ss.str(); 257 | if(ch != '.') { 258 | ss >> t.val; 259 | return; 260 | } 261 | t.kind = Token::Kind::real; 262 | ss << ch; 263 | nextCh(); 264 | while(isdigit(ch)) { 265 | ss << ch; 266 | nextCh(); 267 | } 268 | if(ch != 'e' && ch != 'E') { 269 | ss >> t.valReal; 270 | return; 271 | } 272 | ss << ch; 273 | nextCh(); 274 | if(ch == '-' || ch == '+') { 275 | ss << ch; 276 | nextCh(); 277 | } 278 | while(isdigit(ch)) { 279 | ss << ch; 280 | nextCh(); 281 | } 282 | ss >> t.valReal; 283 | } 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /src/QASMscanner.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #ifndef QASMSCANNER_HPP_ 27 | #define QASMSCANNER_HPP_ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | class QASMscanner { 41 | 42 | 43 | public: 44 | QASMscanner(std::istream& in_stream); 45 | Token next(); 46 | void addFileInput(std::string fname); 47 | 48 | private: 49 | std::istream& in; 50 | std::stack streams; 51 | char ch; 52 | std::map keywords; 53 | int line; 54 | int col; 55 | void nextCh(); 56 | void readName(Token& t); 57 | void readNumber(Token& t); 58 | void readString(Token& t); 59 | void skipComment(); 60 | 61 | class LineInfo { 62 | public: 63 | char ch; 64 | int line, col; 65 | 66 | LineInfo(char ch, int line, int col) { 67 | this->ch = ch; 68 | this->line = line; 69 | this->col = col; 70 | } 71 | }; 72 | 73 | std::stack lines; 74 | 75 | }; 76 | 77 | #endif /* QASMSCANNER_HPP_ */ 78 | -------------------------------------------------------------------------------- /src/QASMsimulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #ifndef QASM_SIMULATOR_H_ 27 | #define QASM_SIMULATOR_H_ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | class QASMsimulator : public Simulator { 35 | public: 36 | QASMsimulator(bool display_statevector, bool display_probabilities); 37 | QASMsimulator(std::string fname, bool display_statevector, bool display_probabilities); 38 | virtual ~QASMsimulator(); 39 | 40 | void Simulate(); 41 | void Simulate(int shots); 42 | void Reset(); 43 | 44 | private: 45 | class Expr { 46 | public: 47 | enum class Kind {number, plus, minus, sign, times, sin, cos, tan, exp, ln, sqrt, div, power, id}; 48 | mpreal num; 49 | Kind kind; 50 | Expr* op1 = NULL; 51 | Expr* op2 = NULL; 52 | std::string id; 53 | 54 | Expr(Kind kind, Expr* op1, Expr* op2, mpreal num, std::string id) { 55 | this->kind = kind; 56 | this->op1 = op1; 57 | this->op2 = op2; 58 | this->num = num; 59 | this->id = id; 60 | } 61 | 62 | Expr(const Expr& expr) { 63 | this->kind = expr.kind; 64 | this->num = expr.num; 65 | this->id = expr.id; 66 | if(expr.op1 != NULL) { 67 | this->op1 = new Expr(*expr.op1); 68 | } 69 | if(expr.op2 != NULL) { 70 | this->op2 = new Expr(*expr.op2); 71 | } 72 | } 73 | 74 | ~Expr() { 75 | if(op1 != NULL) { 76 | delete op1; 77 | } 78 | if(op2 != NULL) { 79 | delete op2; 80 | } 81 | } 82 | }; 83 | 84 | class BasisGate { 85 | public: 86 | virtual ~BasisGate() = default; 87 | }; 88 | 89 | class Ugate : public BasisGate { 90 | public: 91 | Expr* theta; 92 | Expr* phi; 93 | Expr* lambda; 94 | std::string target; 95 | 96 | Ugate(Expr* theta, Expr* phi, Expr* lambda, std::string target) { 97 | this->theta = theta; 98 | this->phi = phi; 99 | this->lambda = lambda; 100 | this->target = target; 101 | } 102 | 103 | ~Ugate() { 104 | if(theta != NULL) { 105 | delete theta; 106 | } 107 | if(phi != NULL) { 108 | delete phi; 109 | } 110 | if(lambda != NULL) { 111 | delete lambda; 112 | } 113 | } 114 | }; 115 | 116 | class CXgate : public BasisGate { 117 | public: 118 | std::string control; 119 | std::string target; 120 | 121 | CXgate(std::string control, std::string target) { 122 | this->control = control; 123 | this->target = target; 124 | } 125 | }; 126 | 127 | class CompoundGate { 128 | public: 129 | std::vector parameterNames; 130 | std::vector argumentNames; 131 | std::vector gates; 132 | bool opaque; 133 | }; 134 | 135 | class Snapshot { 136 | public: 137 | ~Snapshot() { 138 | if(probabilities != NULL) { 139 | delete[] probabilities; 140 | } 141 | if(statevector != NULL) { 142 | delete[] statevector; 143 | } 144 | } 145 | 146 | unsigned long long len; 147 | double* probabilities; 148 | std::string* statevector; 149 | std::map probabilities_ket; 150 | }; 151 | 152 | void scan(); 153 | void check(Token::Kind expected); 154 | 155 | Token la,t; 156 | Token::Kind sym = Token::Kind::none; 157 | 158 | std::string fname; 159 | std::istream* in; 160 | QASMscanner* scanner; 161 | std::map > qregs; 162 | std::map > cregs; 163 | std::pair QASMargumentQreg(); 164 | std::pair QASMargumentCreg(); 165 | Expr* QASMexponentiation(); 166 | Expr* QASMfactor(); 167 | Expr* QASMterm(); 168 | QASMsimulator::Expr* QASMexp(); 169 | void QASMgateDecl(); 170 | void QASMopaqueGateDecl(); 171 | void QASMgate(bool execute = true); 172 | void QASMqop(bool execute = true); 173 | void QASMexpList(std::vector& expressions); 174 | void QASMidList(std::vector& identifiers); 175 | void QASMargsList(std::vector >& arguments); 176 | std::set unaryops {Token::Kind::sin,Token::Kind::cos,Token::Kind::tan,Token::Kind::exp,Token::Kind::ln,Token::Kind::sqrt}; 177 | 178 | QMDD_matrix tmp_matrix; 179 | 180 | std::map compoundGates; 181 | Expr* RewriteExpr(Expr* expr, std::map& exprMap); 182 | void printExpr(Expr* expr); 183 | 184 | 185 | bool display_statevector; 186 | bool display_probabilities; 187 | 188 | std::map snapshots; 189 | }; 190 | 191 | #endif /* QASM_SIMULATOR_H_ */ 192 | -------------------------------------------------------------------------------- /src/QASMtoken.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #include 27 | 28 | std::map Token::KindNames = { 29 | {Kind::none,"none"}, 30 | {Kind::include,"include"}, 31 | {Kind::identifier,""}, 32 | {Kind::number,""}, 33 | {Kind::plus,"+"}, 34 | {Kind::semicolon,";"}, 35 | {Kind::eof,"EOF"}, 36 | {Kind::lpar,"("}, 37 | {Kind::rpar,")"}, 38 | {Kind::lbrack,"["}, 39 | {Kind::rbrack,"]"}, 40 | {Kind::lbrace,"{"}, 41 | {Kind::rbrace,"}"}, 42 | {Kind::comma,","}, 43 | {Kind::minus,"-"}, 44 | {Kind::times,"*"}, 45 | {Kind::nninteger,""}, 46 | {Kind::real,""}, 47 | {Kind::qreg,"qreg"}, 48 | {Kind::creg,"creg"}, 49 | {Kind::ugate,"U"}, 50 | {Kind::cxgate,"CX"}, 51 | {Kind::gate,"gate"}, 52 | {Kind::pi,"pi"}, 53 | {Kind::measure,"measure"}, 54 | {Kind::openqasm,"openqasm"}, 55 | {Kind::probabilities,"probabilities"}, 56 | {Kind::opaque,"opaque"}, 57 | {Kind::sin,"sin"}, 58 | {Kind::cos,"cos"}, 59 | {Kind::tan,"tan"}, 60 | {Kind::exp,"exp"}, 61 | {Kind::ln,"ln"}, 62 | {Kind::sqrt,"sqrt"}, 63 | {Kind::div,"/"}, 64 | {Kind::power,"^"}, 65 | {Kind::string,"string"}, 66 | {Kind::gt,">"}, 67 | {Kind::barrier,"barrier"}, 68 | {Kind::_if,"if"}, 69 | {Kind::eq,"=="}, 70 | {Kind::reset,"reset"} 71 | }; 72 | -------------------------------------------------------------------------------- /src/QASMtoken.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #ifndef TOKEN_H_ 27 | #define TOKEN_H_ 28 | 29 | #include 30 | #include 31 | 32 | class Token { 33 | public: 34 | 35 | enum class Kind {include, none, identifier, number, plus, semicolon, eof, lpar, rpar, lbrack, rbrack, lbrace, rbrace, comma, minus, times, nninteger, real, qreg, creg, ugate, cxgate, gate, pi, measure, openqasm, probabilities, sin, cos, tan, exp, ln, sqrt, div, power, string, gt, barrier, opaque, _if, eq, reset, snapshot}; 36 | 37 | Token(Kind kind, int line, int col) { 38 | this->kind = kind; 39 | this->line = line; 40 | this->col = col; 41 | this->val = 0; 42 | this->valReal = 0.0; 43 | } 44 | 45 | Token() : Token(Kind::none, 0, 0) { 46 | } 47 | 48 | static std::map KindNames; 49 | Kind kind; 50 | int line; 51 | int col; 52 | int val; 53 | double valReal; 54 | std::string str; 55 | 56 | }; 57 | 58 | #endif /* TOKEN_H_ */ 59 | -------------------------------------------------------------------------------- /src/QMDDcircuit.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #include "QMDDcircuit.h" 27 | 28 | using mpfr::mpreal; 29 | 30 | /******************************************************************* 31 | Routines 32 | *****************************************************************/ 33 | 34 | int getlabel(char lab[],QMDDrevlibDescription circ,int *cont) 35 | // get a label and return its index in a global Label table 36 | // cont is returned as 1 (positive control/target), 0 (negative control), or -1 if character after label is '\n' 37 | 38 | // this is a really quick and dirty implementation that uses 39 | // linear searching 40 | { 41 | int i; 42 | char ch = lab[strlen(lab)-1]; // last character of lab 43 | 44 | 45 | *cont = 1; 46 | if (lab[0] == '-'){ // if negative control 47 | *cont = 0; 48 | lab = &lab[1]; 49 | //for (int i=0; i='0'&&ch3<='9') 111 | { 112 | m=m*10+ch3-'0'; 113 | ch3=str[k++]; 114 | } 115 | } 116 | 117 | // for R or Q gate get divisor 118 | if(ch1=='R'||ch1=='Q') 119 | { 120 | sign = 1; 121 | if(ch3!=':') 122 | { 123 | printf("Error when reading line: %s", str); 124 | printf("ch1: %c, ch2: %c, ch3: %c",ch1, ch2, ch3); 125 | throwException("error in R/Q gate spec (missing or misplaced :)\n",0); 126 | } 127 | ch3=str[k++]; 128 | if(ch3=='-') {sign = -1; ch3=str[k++]; } 129 | 130 | double my_div; 131 | //Alwin: Allow real divisor 132 | sscanf(str+k-1, "%lf", &my_div); 133 | div=mpreal(my_div); 134 | //std::cout << "my_div = " << my_div << std::endl; 135 | 136 | while(ch3 != ' ') { 137 | ch3=str[k++]; 138 | } 139 | 140 | /* while(ch3>='0'&&ch3<='9') 141 | { 142 | div=div*10+ch3-'0'; 143 | ch3=str[k++]; 144 | }*/ 145 | 146 | 147 | div *= sign; 148 | //if(VERBOSE) printf("divisor of R/Q gate: %d/%d\n", m,div); 149 | } 150 | 151 | 152 | if (m > circ->n && m!=2) { // too much lines, more than supported by the circuit 153 | printf("Error when reading line: %s", str); 154 | printf("Too much lines (%d)! Circuit only supports %d lines.", m, circ->n); 155 | f.p=NULL; return(f); 156 | } 157 | // define line controls 158 | for(i=0;ingate=1; 177 | else if(m==2||ch1=='C') circ->cgate=1; 178 | else circ->tgate=1; 179 | f=TTlookup(n,m,t,line); 180 | if(f.p==NULL) 181 | { 182 | f=QMDDmvlgate(Nm,n,line); 183 | TTinsert(n,m,t,line,f); 184 | } 185 | (*circ).qcost+=gate_qcost(m,n,TOFFOLI_GATE); 186 | } else if(ch1 == 'M') { 187 | f.p = NULL; 188 | f.w = t+10; 189 | } 190 | else if(ch1=='F') // Fredkin gate 191 | { 192 | circ->fgate=1; 193 | f=QMDDmvlgate(Nm,n,line); 194 | for(i=0;ipgate=1; 204 | f=QMDDmvlgate(Nm,n,line); 205 | line[t]=-1; 206 | line[pc[1]]=2; 207 | f2=QMDDmvlgate(Nm,n,line); 208 | if(ch2==' ') f=QMDDmultiply(f2,f); 209 | else if(ch2=='I') f=QMDDmultiply(f,f2); 210 | else printf("invalid subtype for Peres gate\n"); 211 | (*circ).qcost+=4; // fixed cost for PERES gate 212 | } 213 | else if(ch1=='H') f=QMDDmvlgate(Hm,n,line); // Hadamard gate 214 | else if(ch1=='Z') f=QMDDmvlgate(Zm,n,line); // Pauli-Z gate 215 | else if(ch1=='S') f=QMDDmvlgate(Sm,n,line); // Phase gate 216 | else if(ch1=='0') f=QMDDmvlgate(ZEROm,n,line); // zero pseudo gate 217 | else if(ch1=='V') // V or V+ gate 218 | { 219 | circ->vgate=1; 220 | if(ch2==' ') f=QMDDmvlgate(Vm,n,line); 221 | else if(ch2=='P'||ch2=='+') f=QMDDmvlgate(VPm,n,line); 222 | else { 223 | printf("invalid V subtype '%c'\n",ch2); 224 | throwException("",0); 225 | } 226 | (*circ).qcost+=1; 227 | } 228 | else if(ch1=='Q') 229 | { 230 | // Cfree(Qm[1][1]); 231 | 232 | 233 | Qm[1][1] = Cmake(QMDDcos(1, div.toDouble()), QMDDsin(1, div.toDouble())); 234 | 235 | //TODO this is required for Google Ph gate 236 | /*Qm[1][1] = Cmake(cos(div), sin(div)); 237 | Qm[0][0] = Cmake(cos(div), sin(div));*/ 238 | f=QMDDmvlgate(Qm,n,line); 239 | } 240 | else if(ch1=='R') // Rotation gate 241 | { 242 | //TODO: change if div is floating point number 243 | div *= 2; 244 | if(ch2=='X') 245 | { 246 | m=1; 247 | //TODO 248 | // Cfree(Rm[0][0]);Cfree(Rm[0][1]);Cfree(Rm[1][0]);Cfree(Rm[1][1]); 249 | Rm[0][0] = Cmake(QMDDcos(m, (int)div.toDouble()), COMPLEX_ZERO); 250 | Rm[1][1] = Cmake(QMDDcos(m, (int)div.toDouble()), COMPLEX_ZERO); 251 | Rm[0][1] = Cmake(COMPLEX_ZERO, QMDDsin(m, -div.toDouble())); 252 | Rm[1][0] = Cmake(COMPLEX_ZERO, QMDDsin(m, -div.toDouble())); 253 | 254 | //TODO: required for floating point angles (not a multiple of PI) 255 | /*Rm[0][0] = Cmake(cos(div/2), mpfr_tmp2); 256 | Rm[0][1] = Cmake(mpfr_tmp2, -sin(div/2)); 257 | Rm[1][0] = Cmake(mpfr_tmp2, -sin(div/2)); 258 | Rm[1][1] = Cmake(cos(div/2), mpfr_tmp2);*/ 259 | } else if(ch2=='Y') 260 | { 261 | //TODO m=1 262 | m=1; 263 | // Cfree(Rm[0][0]);Cfree(Rm[0][1]);Cfree(Rm[1][0]);Cfree(Rm[1][1]); 264 | Rm[0][0] = Cmake(QMDDcos(m, (int)div.toDouble()), COMPLEX_ZERO); 265 | Rm[1][1] = Cmake(QMDDcos(m, (int)div.toDouble()), COMPLEX_ZERO); 266 | Rm[0][1] = Cmake(QMDDsin(m, (int)-div.toDouble()), COMPLEX_ZERO); 267 | Rm[1][0] = Cmake(QMDDsin(m, (int)div.toDouble()), COMPLEX_ZERO); 268 | 269 | //TODO: required for floating point angles (not a multiple of PI) 270 | /*Rm[0][0] = Cmake(cos(div/2), mpfr_tmp2); 271 | Rm[0][1] = Cmake(-sin(div/2),mpfr_tmp2); 272 | Rm[1][0] = Cmake(sin(div/2),mpfr_tmp2); 273 | Rm[1][1] = Cmake(cos(div/2), mpfr_tmp2);*/ 274 | } else if(ch2=='Z') 275 | { 276 | m=1; 277 | // Cfree(Rm[0][0]);Cfree(Rm[0][1]);Cfree(Rm[1][0]);Cfree(Rm[1][1]); 278 | Rm[0][0] = Cmake(QMDDcos(m, div.toDouble()), QMDDsin(m, -div.toDouble())); 279 | Rm[0][1] = COMPLEX_ZERO; 280 | Rm[1][0] = COMPLEX_ZERO; 281 | Rm[1][1] = Cmake(QMDDcos(m, div.toDouble()), QMDDsin(m, div.toDouble())); 282 | 283 | //TODO: required for floating point angles (not a multiple of PI) 284 | /*Rm[0][0] = Cmake(cos(div/2), -sin(div/2)); 285 | Rm[0][1] = Cmake(0,0); 286 | Rm[1][0] = Cmake(0,0); 287 | Rm[1][1] = Cmake(cos(div/2), sin(div/2));*/ 288 | } else { 289 | printf("invalid rotation type '%c'\n",ch2); 290 | throwException("",0); 291 | } 292 | f=QMDDmvlgate(Rm,n,line); // do rotation gate 293 | } else { 294 | printf("invalid gate type '%c'\n",ch1); 295 | throwException("",0); 296 | } 297 | } 298 | 299 | return(f); 300 | } 301 | 302 | 303 | QMDDedge QMDDreadGate(FILE *infile,QMDDrevlibDescription *circ) 304 | { 305 | 306 | char lineFromInfile[512]; // read one line from the FILE 307 | char ch1; 308 | 309 | ch1=getch(infile); 310 | // skip characters until the first meaningful is read 311 | while(ch1==' '||ch1=='\n'||ch1=='#') { 312 | if (ch1=='#') 313 | skip2eol(infile); 314 | ch1=getch(infile); 315 | } 316 | lineFromInfile[0] = ch1; 317 | getline(infile,&lineFromInfile[1]); 318 | 319 | if(0==strcmp(lineFromInfile,"ECHO\n")) { 320 | QMDDedge e; 321 | e.p = NULL; 322 | e.w = COMPLEX_M_ONE; 323 | return e; 324 | } 325 | if(0==strcmp(lineFromInfile,"PUSH\n")) { 326 | QMDDedge e; 327 | e.p = NULL; 328 | e.w = 3; 329 | return e; 330 | } 331 | if(0==strcmp(lineFromInfile,"POP\n")) { 332 | QMDDedge e; 333 | e.p = NULL; 334 | e.w = 4; 335 | return e; 336 | } 337 | 338 | //printf("readGateFromString: %s\n",lineFromInfile); 339 | 340 | return QMDDreadGateFromString(lineFromInfile,circ); 341 | 342 | } 343 | 344 | /*******************************************************************************/ 345 | 346 | QMDDrevlibDescription QMDDrevlibHeader(FILE *infile) 347 | { 348 | int header,n,p,i; 349 | char cmd[MAXSTRLEN]; //,tLabel[MAXSTRLEN],tInput[MAXSTRLEN]; 350 | char ch,ch1; 351 | // CircuitLine temp; 352 | QMDDrevlibDescription circ; 353 | 354 | circ.nancillary=circ.ngarbage=0; 355 | header=1; 356 | if(VERBOSE) printf("Reading header"); 357 | while(header) 358 | { 359 | ch=getch(infile); 360 | if(VERBOSE) printf("%c",ch); 361 | if(ch=='#') skip2eol(infile); 362 | else { 363 | while(ch==' '||ch=='\n') ch=getch(infile); 364 | if(ch!='.') 365 | { 366 | printf("invalid file:\n"); 367 | circ.n=0; 368 | return(circ); 369 | } 370 | getstr(infile,cmd); 371 | if(0==strcmp(cmd,"BEGIN")) header=0; // end of header information 372 | else if(0==strcmp(cmd,"VERSION")) 373 | { 374 | ch1=getch(infile); 375 | while(ch1==' ') ch1=getch(infile); 376 | p=0; 377 | while(ch1!='\n') 378 | { 379 | circ.version[p++]=ch1; 380 | ch1=getch(infile); 381 | } 382 | circ.version[p]=0; 383 | } else if(0==strcmp(cmd,"NUMVARS")) 384 | { 385 | circ.n=n=getint(infile); 386 | if(VERBOSE) printf("\nnumber of variables %d\n",n); 387 | } else if(0==strcmp(cmd,"VARIABLES")) 388 | { 389 | for(p=n-1;p>=0;p--) 390 | { 391 | getstr(infile,circ.line[p].variable); 392 | strcpy(circ.line[p].input,circ.line[p].variable); // set inputs to variable names 393 | strcpy(circ.line[p].input,circ.line[p].variable); // set output to variable names 394 | circ.line[p].ancillary=circ.line[p].garbage='-'; // by default no lines are ancillaries or garbage 395 | } 396 | Nlabel=n; 397 | } else if(0==strcmp(cmd,"INPUTS")) 398 | { 399 | for(p=n-1;p>=0;p--) 400 | { 401 | getstr(infile,circ.line[p].input); 402 | } 403 | } else if(0==strcmp(cmd,"OUTPUTS")) 404 | { 405 | for(p=n-1;p>=0;p--) 406 | { 407 | getstr(infile,circ.line[p].output); 408 | } 409 | } else if(0==strcmp(cmd,"CONSTANTS")) 410 | { 411 | for(p=n-1;p>=0;p--) 412 | { 413 | circ.line[p].ancillary=getnbch(infile); 414 | if(circ.line[p].ancillary!='-') circ.nancillary++; 415 | } 416 | skip2eol(infile); 417 | } else if(0==strcmp(cmd,"GARBAGE")) 418 | { 419 | for(p=n-1;p>=0;p--) 420 | { 421 | circ.line[p].garbage=getnbch(infile); 422 | if(circ.line[p].garbage!='-') circ.ngarbage++; 423 | } 424 | skip2eol(infile); 425 | } else if(0==strcmp(cmd,"DEFINE")) 426 | { 427 | while(strcmp(cmd,"ENDDEFINE")) 428 | { 429 | skip2eol(infile); 430 | ch1=getch(infile); 431 | getstr(infile,cmd); 432 | } 433 | //skip2eol(infile); 434 | } 435 | } 436 | } 437 | 438 | if(VERBOSE) printf("completed.\n"); 439 | for(i=0;i0) circ.dc[i++]='C'; 534 | if(circ.ngarbage>0) circ.dc[i++]='G'; 535 | circ.dc[i]=0; 536 | 537 | fclose(infile); 538 | return(circ); 539 | } 540 | 541 | 542 | -------------------------------------------------------------------------------- /src/QMDDcircuit.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | 27 | #ifndef QMDDcircuit_H 28 | #define QMDDcircuit_H 29 | 30 | #include 31 | 32 | #include "QMDDpackage.h" 33 | #include "QMDDcomplex.h" 34 | #include "qcost.h" 35 | #include "textFileUtilities.h" 36 | #include 37 | #include 38 | 39 | /***************************************************************** 40 | 41 | Routines 42 | *****************************************************************/ 43 | 44 | int getlabel(char*,QMDDrevlibDescription,int*); 45 | 46 | QMDDedge QMDDreadGateFromString(char*, QMDDrevlibDescription*); 47 | QMDDedge QMDDreadGate(FILE*,QMDDrevlibDescription*); 48 | QMDDrevlibDescription QMDDrevlibHeader(FILE*); 49 | QMDDrevlibDescription QMDDcircuitRevlib(char *fname,QMDDrevlibDescription firstCirc,int match); 50 | // reads a circuit in Revlib format: http://www.revlib.org/documentation.php 51 | 52 | 53 | /*******************************************************************************/ 54 | #endif 55 | -------------------------------------------------------------------------------- /src/QMDDcomplex.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | 27 | #ifndef QMDDcomplex_H 28 | #define QMDDcomplex_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | //#include 39 | 40 | 41 | 42 | #include 43 | #include 44 | 45 | 46 | using mpfr::mpreal; 47 | 48 | #ifndef DEFINE_COMPLEX_H_VARIABLES 49 | #define EXTERN_C extern 50 | #else 51 | #define EXTERN_C 52 | #endif 53 | 54 | typedef struct 55 | { 56 | // long double r,i; 57 | mpfr_t r; 58 | mpfr_t i; 59 | } complex; 60 | 61 | #include "QMDDpackage.h" 62 | 63 | extern mpreal Ctol; 64 | 65 | 66 | void Cprint(uint64_t, std::ostream&); 67 | void Cprint(uint64_t); // print a complex value to STD_OUT 68 | 69 | /********************************************* 70 | 71 | Complex computation tables. These tables save 72 | result of computations over complex values to 73 | avoid recomputation later. 74 | 75 | Cta - addition 76 | Cts - subtraction 77 | Ctm - multiplication 78 | Ctd - division 79 | 80 | *********************************************/ 81 | 82 | struct pair_hash 83 | { 84 | std::size_t operator()(std::pair p) const 85 | { 86 | using std::size_t; 87 | using std::hash; 88 | 89 | return hash()(p.first) ^ hash()(p.second); 90 | } 91 | }; 92 | 93 | EXTERN_C std::unordered_map< std::pair, uint64_t, pair_hash> cta,cts,ctm,ctd; 94 | 95 | 96 | complex Cvalue(uint64_t x); 97 | 98 | int Cgt(uint64_t,uint64_t); // greater than 99 | int Clt(uint64_t, uint64_t); // analogous to Cgt 100 | 101 | void QMDDinitCtable(void); // initialize the complex value table and complex operation tables to empty 102 | void QMDDcomplexInit(void); // initialization 103 | 104 | 105 | void QMDDcvalue_table_list(void); // print the complex value table entries 106 | uint64_t Clookup(complex&); // lookup a complex value in the complex value table; if not found add it 107 | 108 | uint64_t Conj(uint64_t); /// return complex conjugate 109 | std::pair approx(uint64_t t); 110 | 111 | // basic operations on complex values 112 | // meanings are self-evident from the names 113 | // NOTE arguments are the indices to the values 114 | // in the complex value table not the values themselves 115 | 116 | uint64_t Cnegative(uint64_t); 117 | uint64_t Cadd(uint64_t,uint64_t); 118 | uint64_t Csub(uint64_t,uint64_t); 119 | uint64_t Cmul(uint64_t,uint64_t); 120 | uint64_t CintMul(int, uint64_t); // multiply by an integer 121 | uint64_t Cdiv(uint64_t,uint64_t); 122 | void QMDDmakeRootsOfUnity(void); 123 | uint64_t CAbs(uint64_t); /// by PN: returns the absolut value of a complex number 124 | int CUnit(uint64_t a); ///by PN: returns whether a complex number has norm 1 125 | 126 | //void Cfree(complex&); 127 | 128 | 129 | #define COMPLEX_ZERO 0x0ull 130 | #define COMPLEX_ONE 0x0000000100000000ull 131 | #define COMPLEX_M_ONE 0x8000000100000000ull 132 | 133 | //Czero is used in QMDDpackage.cpp and in QMDDcircuit.cpp to initialize the gate matrices (in combination with Cmake) 134 | //#define Czero 0 135 | #define PREC 200 136 | 137 | EXTERN_C std::unordered_map Ctable; // value 138 | EXTERN_C std::unordered_map Cmag; //mpfr_t /*long double*/ Cmag[COMPLEXTSIZE]; // magnitude to avoid repeated computation 139 | 140 | mpreal QMDDcos(int fac, double div); 141 | mpreal QMDDsin(int fac, double div); 142 | void angle(mpfr_t, int); // computes angle for polar coordinate representation 143 | uint64_t Cmake(mpreal, mpreal); // make a complex value 144 | mpreal Qmake(int,int,int); // returns the complex number equal to (a+b*sqrt(2))/c 145 | // required to be compatible with quadratic irrational-based 146 | // complex number package 147 | 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /src/QMDDcore.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | //#include "qcost.c" 33 | #include "qcost.h" 34 | //#include "timing.c" 35 | #include "timing.h" 36 | //#include "textFileUtilities.c" // basic text handling routines 37 | #include "textFileUtilities.h" // basic text handling routines 38 | #include "QMDDpackage.h" // constants, type definitions and globals 39 | #include "QMDDcomplex.h" 40 | //#ifdef __HADAMARD__ 41 | //#include "QMDDcomplexH.c" // complex Hadamard number package 42 | //#else 43 | //#include "QMDDcomplexD.c" // complex number package 44 | //#endif 45 | //#include "QMDDpackage.c" // QMDD package 46 | //#ifdef __SignatureSynthesis__ 47 | //#include "QMDDsigstuff.c" // include signature stuff 48 | //#endif 49 | #include "QMDDreorder.h" // sifting 50 | #include "QMDDcircuit.h" // procedures for building a QMDD from a circuit file 51 | 52 | -------------------------------------------------------------------------------- /src/QMDDpackage.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #ifndef QMDDpackage_H 27 | #define QMDDpackage_H 28 | 29 | #include "external.h" 30 | 31 | #define QMDDversion "QMDD Package V.R1 September 2015\n" 32 | 33 | // problem parameter limits 34 | 35 | #define MAXSTRLEN 11 36 | #define MAXN 300 // max no. of inputs 37 | #define MAXRADIX 2 // max logic radix 38 | #define MAXNEDGE 4 // max no. of edges = MAXRADIX^2 39 | #define MAXNODECOUNT 2000000 // max number of nodes in a QMDD for counting 40 | #define GCLIMIT1 25000 // first garbage collection limit 41 | #define GCLIMIT_INC 0 // garbage collection limit increment 42 | // added to garbage collection limit after each collection 43 | #define MAXND 5 // max n for display purposes 44 | #define MAXDIM 32 // max dimension of matrix for printing, (should be 2^MAXND) 45 | #define NBUCKET 32768 //8192 // no. of hash table buckets; must be a power of 2 46 | #define HASHMASK 32767 //8191 // must be nbuckets-1 47 | #define CTSLOTS 16384 // no. of computed table slots 48 | #define CTMASK 16383 // must be CTSLOTS-1 49 | #define COMPLEXTSIZE 100000 // complex table size 50 | #define COMPLEXTMASK 127 // complex table index mask (not used anywhere?!) 51 | #define TTSLOTS 2048 // Toffoli table slots 52 | #define TTMASK 2047 // must be TTSLOTS-1 53 | #define MAXREFCNT 4000000 // max reference count (saturates at this value) 54 | #define MAXPL 65536 // max size for a permutation recording 55 | 56 | #define DYNREORDERLIMIT 500 // minimum value for dynamic reordering limit 57 | #define VERBOSE 0 58 | 59 | //#include 60 | #include 61 | #include "QMDDcomplex.h" 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | 69 | #include 70 | //#include 71 | 72 | // edge and node definitions 73 | 74 | #define DOT_USE_CMAG 0 75 | #define DOT_OUTPUT_VECTOR 0 76 | 77 | 78 | typedef struct QMDDnode *QMDDnodeptr; 79 | 80 | typedef struct QMDDedge 81 | { 82 | QMDDnodeptr p; // edge pointer 83 | uint64_t w; 84 | 85 | // unsigned int w; // index of weight edge in complex value table 86 | // int sentinel; // in 64-bit architecture sizeof(QMDDedge) is a multiple of 8 (8 Byte = 64 bit). 87 | // So sizeof() is 16 with AND without this sentinel. thus when using memcmp identical nodes may not be identified. 88 | } QMDDedge; 89 | 90 | typedef struct QMDDnode 91 | { 92 | QMDDnodeptr next; // link for unique table and available space chain 93 | unsigned int ref; // reference count 94 | unsigned char v; // variable index (nonterminal) value (-1 for terminal) 95 | uint64_t renormFactor; // factor that records renormalization factor 96 | char ident,diag,block,symm,c01; // flag to mark if vertex heads a QMDD for a special matrix 97 | char computeSpecialMatricesFlag; // flag to mark whether SpecialMatrices are to be computed 98 | //QMDDedge e[0]; // edges out of this node - variable so must be last in structure 99 | QMDDedge e[MAXNEDGE]; // when calling malloc in QMDDgetnode 100 | } QMDDnode; 101 | 102 | // list definitions for breadth first traversals (e.g. printing) 103 | typedef struct ListElement *ListElementPtr; 104 | 105 | typedef struct ListElement 106 | { 107 | int w,cnt; 108 | int line[MAXN]; 109 | QMDDnodeptr p; 110 | ListElementPtr next; 111 | } ListElement; 112 | 113 | // computed table definitions 114 | 115 | typedef enum{add,mult,kronecker,reduce,transpose,conjugateTranspose,transform,c0,c1,c2,none,norm,createHdmSign,findCmnSign,findBin,reduceHdm, renormalize} CTkind; // compute table entry kinds 116 | 117 | typedef struct CTentry// computed table entry defn 118 | { 119 | QMDDedge a,b,r; // a and b are arguments, r is the result 120 | CTkind which; // type of operation 121 | } CTentry; 122 | 123 | typedef struct TTentry // Toffoli table entry defn 124 | { 125 | int n,m,t,line[MAXN]; 126 | QMDDedge e; 127 | } TTentry; 128 | 129 | typedef struct CircuitLine 130 | { 131 | char input[MAXSTRLEN]; 132 | char output[MAXSTRLEN]; 133 | char variable[MAXSTRLEN]; 134 | char ancillary; 135 | char garbage; 136 | 137 | } CircuitLine; 138 | 139 | typedef struct QMDDrevlibDescription // circuit description structure 140 | { 141 | int n,ngates,qcost,nancillary,ngarbage; 142 | QMDDedge e,totalDC; 143 | CircuitLine line[MAXN]; 144 | char version[MAXSTRLEN]; 145 | char inperm[MAXN],outperm[MAXN]; 146 | char ngate,cgate,tgate,fgate,pgate,vgate,kind[7],dc[5],name[32],no[8],modified; 147 | } QMDDrevlibDescription; 148 | 149 | 150 | typedef uint64_t QMDD_matrix[MAXRADIX][MAXRADIX]; 151 | 152 | /* GLOBALS */ 153 | 154 | EXTERN QMDD_matrix Nm,Vm,VPm,Sm,Rm,Hm,Zm,ZEROm,Qm; 155 | 156 | 157 | 158 | /*************************************** 159 | 160 | Global variables 161 | 162 | ***************************************/ 163 | EXTERN void cleanCtable(std::vector save_edges); 164 | 165 | 166 | #ifndef DEFINE_VARIABLES 167 | EXTERN int Radix; // radix (default is 2) 168 | 169 | EXTERN int Nedge; // no. of edges (default is 4) 170 | #endif 171 | 172 | EXTERN QMDDnodeptr Avail; // pointer to available space chain 173 | 174 | EXTERN ListElementPtr Lavail; // pointer to available list elements for breadth first searchess 175 | 176 | EXTERN QMDDnodeptr QMDDtnode; // pointer to terminal node 177 | 178 | EXTERN QMDDedge QMDDone,QMDDzero; // edges pointing to zero and one QMDD constants 179 | 180 | 181 | EXTERN int64_t QMDDorder[MAXN]; // variable order initially 0,1,... from bottom up | Usage: QMDDorder[level] := varible at a certain level 182 | EXTERN int64_t QMDDinvorder[MAXN]; // inverse of variable order (inverse permutation) | Usage: QMDDinvorder[variable] := level of a certain variable 183 | 184 | EXTERN int64_t QMDDnodecount; // counts active nodes 185 | EXTERN int64_t QMDDpeaknodecount; // records peak node count in unique table 186 | 187 | EXTERN int64_t Ncount; // used in QMDD node count - very naive approach 188 | EXTERN QMDDnodeptr Nlist[MAXNODECOUNT]; 189 | 190 | EXTERN int64_t Nop[6]; // operation counters 191 | 192 | EXTERN int64_t CTlook[20],CThit[20]; // counters for gathering compute table hit stats 193 | 194 | EXTERN int64_t UTcol, UTmatch, UTlookups; // counter for collisions / matches in hash tables 195 | EXTERN int64_t UTkeys[NBUCKET]; 196 | 197 | EXTERN int GCcurrentLimit; // current garbage collection limit 198 | 199 | EXTERN int ActiveNodeCount; // number of active nodes 200 | 201 | EXTERN int Active[MAXN]; // number of active nodes for each variable 202 | 203 | #ifndef DEFINE_VARIABLES 204 | EXTERN int GCswitch; // set switch to 1 to enable garbage collection 205 | 206 | EXTERN int Smode; // S mode switch for spectral transformation 207 | // Smode==1 0->+1 1->-1; Smode==0 0->0 1->1 208 | 209 | EXTERN int RMmode; // Select RM transformation mode 210 | // forces mod Radix arithmetic 211 | 212 | 213 | EXTERN int MultMode; // set to 1 for matrix - vector multiplication 214 | #endif 215 | 216 | EXTERN QMDDedge QMDDnullEdge; // set in QMDDinit routine 217 | 218 | EXTERN int PermList[MAXPL]; // array for recording a permutation 219 | 220 | // for sifting 221 | #ifndef DEFINE_VARIABLES 222 | EXTERN int RenormalizationNodeCount; // number of active nodes that need renormalization (used in QMDDdecref) 223 | EXTERN int blockMatrixCounter; // number of active nodes that represent block matrices (used in QMDDincref, QMDDdecref) 224 | EXTERN char globalComputeSpecialMatricesFlag; // default value for computeSpecialMatricesFlag of newly created nodes (used in QMDDmakeNonterminal) 225 | EXTERN int dynamicReorderingTreshold; 226 | 227 | EXTERN int largestRefCount; 228 | #endif 229 | /******************************************* 230 | 231 | Unique Tables (one per input variable) 232 | 233 | *******************************************/ 234 | 235 | EXTERN QMDDnodeptr Unique[MAXN][NBUCKET]; 236 | 237 | /**************************************************** 238 | 239 | Compute Table (only one for all operation types) 240 | 241 | ****************************************************/ 242 | 243 | //EXTERN CTentry CTable[CTSLOTS]; 244 | 245 | struct computeKey 246 | { 247 | QMDDedge a,b; 248 | 249 | }; 250 | 251 | inline bool operator==(const computeKey& lhs, const computeKey& rhs) 252 | { 253 | return (memcmp(&lhs, &rhs, sizeof(computeKey)) == 0); 254 | } 255 | 256 | struct computeHasher 257 | { 258 | std::size_t operator()(const computeKey& k) const 259 | { 260 | using std::size_t; 261 | using std::hash; 262 | 263 | return hash()((int64_t)k.a.p) ^ hash()((int64_t)k.b.p>>3) ^ hash()((int64_t)k.a.w) ^ hash()((int64_t)k.b.w); 264 | } 265 | }; 266 | 267 | EXTERN std::unordered_map< computeKey, QMDDedge, computeHasher > CTable_add, CTable_mult, CTable_transpose, CTable_conjugateTranspose, CTable_renormalize; 268 | 269 | 270 | /**************************************************** 271 | 272 | Toffoli gate table 273 | 274 | *****************************************************/ 275 | 276 | EXTERN TTentry TTable[TTSLOTS]; 277 | 278 | /**************************************************** 279 | 280 | Identity matrix table 281 | 282 | ****************************************************/ 283 | 284 | EXTERN QMDDedge QMDDid[MAXN]; 285 | 286 | /**************************************************** 287 | 288 | Variable labels 289 | 290 | ****************************************************/ 291 | 292 | EXTERN int Nlabel; // number of labels 293 | EXTERN char Label[MAXN][MAXSTRLEN]; // label table 294 | 295 | /**************************************************** 296 | 297 | Output file for writing permutation 298 | 299 | ****************************************************/ 300 | 301 | EXTERN FILE *outfile; 302 | 303 | 304 | 305 | #ifdef DEFINE_VARIABLES 306 | 307 | // initalize global variables 308 | int Radix = MAXRADIX; // radix (default is 2) 309 | int Nedge=MAXNEDGE; // no. of edges (default is 4) 310 | int GCswitch = 1; // set switch to 1 to enable garbage collection 311 | int Smode = 1; // S mode switch for spectral transformation 312 | // Smode==1 0->+1 1->-1; Smode==0 0->0 1->1 313 | int RMmode =0; // Select RM transformation mode 314 | int MultMode = 0; // set to 1 for matrix - vector multiplication 315 | 316 | // for sifting 317 | 318 | int RenormalizationNodeCount = 0; // number of active nodes that need renormalization (used in QMDDdecref) 319 | int blockMatrixCounter = 0; // number of active nodes that represent block matrices (used in QMDDincref, QMDDdecref) 320 | char globalComputeSpecialMatricesFlag = 1; // default value for computeSpecialMatricesFlag of newly created nodes (used in QMDDmakeNonterminal) 321 | int dynamicReorderingTreshold = DYNREORDERLIMIT; 322 | 323 | int largestRefCount = 0; 324 | 325 | #else 326 | 327 | 328 | #endif 329 | 330 | 331 | /*************************************** 332 | 333 | Public Macros 334 | 335 | ***************************************/ 336 | 337 | 338 | #define __NormC__ // must be __NormA__ for leftmost-nonzero normalization and __NormB__ for leftmost absolut-value normalization 339 | //ZULEHNER: NORMC -> normalize for largest (abs value) (numerically more stable) 340 | 341 | #define QMDDterminal(e) (e.p==QMDDtnode) // checks if an edge points to the terminal node 342 | 343 | #define QMDDedgeEqual(a,b) ((a.p==b.p)&&(a.w==b.w)) // checks if two edges are equal 344 | 345 | 346 | 347 | 348 | 349 | /** Routines 350 | * 351 | */ 352 | 353 | void throwException(const char*, int); 354 | 355 | void QMDDpause(void); 356 | void QMDDdebugnode(QMDDnodeptr); // for debugging purposes - not normally used 357 | ListElementPtr QMDDnewListElement(void); 358 | void QMDDprint(QMDDedge,int); 359 | void QMDD2dot(QMDDedge,int, std::ostream&, QMDDrevlibDescription); 360 | QMDDedge QMDDmultiply(QMDDedge,QMDDedge); 361 | QMDDedge QMDDadd(QMDDedge,QMDDedge); 362 | QMDDedge QMDDkron(QMDDedge,QMDDedge); 363 | void QMDDdecref(QMDDedge); 364 | void QMDDincref(QMDDedge); 365 | QMDDedge QMDDident(int,int); 366 | QMDDedge QMDDmvlgate(QMDD_matrix,int ,int[]); 367 | void TTinsert(int,int,int,int[],QMDDedge); 368 | QMDDedge TTlookup(int,int,int,int[]); 369 | void QMDDgarbageCollect(void); 370 | QMDDedge QMDDtranspose(QMDDedge); //prototype 371 | void QMDDmatrixPrint2(QMDDedge); // prototype 372 | QMDDedge QMDDnormalize(QMDDedge); 373 | void QMDDinitGateMatrices(void); 374 | void QMDDcheckSpecialMatrices(QMDDedge); 375 | QMDDedge CTlookup(QMDDedge,QMDDedge,CTkind); 376 | void CTinsert(QMDDedge,QMDDedge,QMDDedge,CTkind); 377 | void QMDDinitComputeTable(void); 378 | QMDDedge QMDDutLookup(QMDDedge); 379 | QMDDedge QMDDmakeNonterminal(short,QMDDedge[]); 380 | //QMDDedge QMDDmakeTerminal(complex); 381 | QMDDedge QMDDmakeTerminal(uint64_t); 382 | void QMDDinit(int verbose); 383 | void QMDDdotExport(QMDDedge basic, int n, char outputFilename[], QMDDrevlibDescription circ, int show); 384 | void QMDDstatistics(void); 385 | QMDDedge QMDDconjugateTranspose(QMDDedge a); 386 | QMDDedge QMDDtrace(QMDDedge a, unsigned char var, char remove[], char all); 387 | void QMDDprintActive(int n); 388 | #endif 389 | -------------------------------------------------------------------------------- /src/QMDDreorder.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | 27 | #ifndef QMDDreorder_H 28 | #define QMDDreorder_H 29 | 30 | //#include 31 | 32 | #include "QMDDpackage.h" 33 | //#include "QMDDcomplex.h" 34 | #include "timing.h" 35 | //#include "textFileUtilities.h" 36 | //#include 37 | //#include 38 | 39 | /***************************************************************** 40 | 41 | Routines 42 | *****************************************************************/ 43 | 44 | int siftingCostFunction(QMDDedge a); 45 | 46 | int checkandsetBlockProperty(QMDDedge a); 47 | int checkBlockMatrices(QMDDedge a, int triggerValue); 48 | void QMDDrestoreSpecialMatrices(QMDDedge a); 49 | void QMDDmarkupSpecialMatrices(QMDDedge a); 50 | void QMDDresetVertexWeights(QMDDedge a, uint64_t standardValue); 51 | 52 | QMDDedge QMDDbuildIntermediate(QMDDedge a); 53 | QMDDedge QMDDrenormalize(QMDDedge a); 54 | void QMDDchangeNonterminal(short v,QMDDedge edge[],QMDDnodeptr p); 55 | int QMDDcheckDontCare(QMDDnodeptr p, int v2); 56 | void QMDDswapNode(QMDDnodeptr p,int v1,int v2, int swap); 57 | void QMDDswap(int i); 58 | int QMDDsift(int n, QMDDedge *root, QMDDrevlibDescription *circ, std::ostream &os); 59 | int myQMDDsift(int n, QMDDedge *root, QMDDrevlibDescription *circ, std::ostream &os, int lowerbound, int upperbound); 60 | int QMDDsift(int n, QMDDedge *root, QMDDrevlibDescription *circ); 61 | int lookupLabel(char buffer[], char moveLabel[], QMDDrevlibDescription *circ); 62 | void QMDDreorder(int order[],int n, QMDDedge *root); 63 | void myQMDDreorder(int order[],int n, QMDDedge *root); 64 | int QMDDmoveVariable(QMDDedge *basic, char buffer[], QMDDrevlibDescription *circ); 65 | void SJTalgorithm(QMDDedge a, int n); 66 | /*******************************************************************************/ 67 | #endif 68 | -------------------------------------------------------------------------------- /src/Simulator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #include 27 | 28 | Simulator::Simulator() { 29 | // TODO Auto-generated constructor stub 30 | epsilon = mpreal(0.01); 31 | for(int i = 0; i < MAXN; i++) { 32 | line[i] = -1; 33 | } 34 | circ.e = QMDDone; 35 | QMDDincref(circ.e); 36 | beforeMeasurement = QMDDone; 37 | QMDDincref(beforeMeasurement); 38 | circ.n = 0; 39 | } 40 | 41 | Simulator::~Simulator() { 42 | // TODO Auto-generated destructor stub 43 | } 44 | 45 | void Simulator::Reset() { 46 | QMDDdecref(circ.e); 47 | QMDDgarbageCollect(); 48 | cleanCtable(std::vector()); 49 | nqubits = 0; 50 | circ.e = QMDDone; 51 | QMDDincref(circ.e); 52 | beforeMeasurement = QMDDone; 53 | QMDDincref(beforeMeasurement); 54 | circ.n = 0; 55 | max_active = 0; 56 | complex_limit = 10000; 57 | gatecount = 0; 58 | max_gates = 0x7FFFFFFF; 59 | intermediate_measurement = false; 60 | measurement_done = false; 61 | } 62 | 63 | void Simulator::AddVariables(int add, std::string name) { 64 | QMDDedge f = QMDDone; 65 | QMDDedge edges[4]; 66 | edges[1]=edges[2]=edges[3]=QMDDzero; 67 | 68 | for(int p=0;p= 0; i--) { 82 | strncpy(circ.line[i+add].variable, circ.line[i].variable, MAXSTRLEN); 83 | } 84 | } 85 | for(int i = 0; i < add; i++) { 86 | snprintf(circ.line[nqubits + add - 1 - i].variable, MAXSTRLEN , "%s[%d]",name.c_str(), i); 87 | } 88 | 89 | nqubits += add; 90 | circ.n = nqubits; 91 | if(!measurement_done) { 92 | QMDDdecref(beforeMeasurement); 93 | beforeMeasurement = circ.e; 94 | QMDDincref(beforeMeasurement); 95 | } 96 | } 97 | 98 | QMDDedge Simulator::AddVariablesRec(QMDDedge e, QMDDedge t, int add) { 99 | 100 | if(e.p == QMDDtnode) { 101 | if(e.w == 0) { 102 | return QMDDzero; 103 | } 104 | t.w = Cmul(e.w, t.w); 105 | return t; 106 | } 107 | 108 | std::map::iterator it = dag_edges.find(e.p); 109 | if(it != dag_edges.end()) { 110 | QMDDedge e2 = it->second; 111 | e2.w = Cmul(e.w, e2.w); 112 | return e2; 113 | } 114 | 115 | QMDDedge edges[MAXRADIX*MAXRADIX]; 116 | 117 | for(int i=0; ie[i], t, add); 119 | } 120 | 121 | QMDDedge e2 = QMDDmakeNonterminal(e.p->v+add, edges); 122 | dag_edges[e.p] = e2; 123 | e2.w = Cmul(e.w, e2.w); 124 | return e2; 125 | } 126 | 127 | mpreal Simulator::AssignProbs(QMDDedge& e) { 128 | std::unordered_map::iterator it2; 129 | std::unordered_map::iterator it = probs.find(e.p); 130 | if(it != probs.end()) { 131 | it2 = Cmag.find(e.w & 0x7FFFFFFF7FFFFFFFull); 132 | return it2->second * it2->second * it->second; 133 | } 134 | mpreal sum; 135 | if(QMDDterminal(e)) { 136 | sum = mpreal(1); 137 | } else { 138 | sum = AssignProbs(e.p->e[0]) + AssignProbs(e.p->e[2]); //+ AssignProbs(e.p->e[1]) + AssignProbs(e.p->e[3]); 139 | } 140 | 141 | probs.insert(std::pair(e.p, sum)); 142 | 143 | it2 = Cmag.find(e.w & 0x7FFFFFFF7FFFFFFFull); 144 | 145 | return it2->second * it2->second * sum; 146 | } 147 | 148 | void Simulator::MeasureAll(bool reset_state) { 149 | std::unordered_map::iterator it; 150 | std::unordered_map::iterator it2; 151 | 152 | probs.clear(); 153 | 154 | mpreal p,p0,p1,tmp,w; 155 | p = AssignProbs(circ.e); 156 | 157 | if(abs(p -1) > epsilon) { 158 | if(p == 0) { 159 | std::cerr << "ERROR: numerical instabilities led to a 0-vector! Abort simulation!" << std::endl; 160 | exit(1); 161 | } 162 | std::cerr << "WARNING in measurement: numerical instability occurred during simulation: |alpha|^2 + |beta|^2 = " << p << ", but should be 1!"<< std::endl; 163 | } 164 | 165 | QMDDedge cur = circ.e; 166 | for(int i = QMDDinvorder[circ.e.p->v]; i >= 0;--i) { 167 | 168 | it = probs.find(cur.p->e[0].p); 169 | it2 = Cmag.find(cur.p->e[0].w & 0x7FFFFFFF7FFFFFFFull); 170 | p0 = it->second * it2->second * it2->second; 171 | it2 = Cmag.find(cur.p->e[1].w & 0x7FFFFFFF7FFFFFFFull); 172 | it = probs.find(cur.p->e[1].p); 173 | p0 += it->second * it2->second * it2->second; 174 | 175 | it = probs.find(cur.p->e[2].p); 176 | it2 = Cmag.find(cur.p->e[2].w & 0x7FFFFFFF7FFFFFFFull); 177 | p1 = it->second * it2->second * it2->second; 178 | it = probs.find(cur.p->e[3].p); 179 | it2 = Cmag.find(cur.p->e[3].w & 0x7FFFFFFF7FFFFFFFull); 180 | p1 += it->second * it2->second * it2->second; 181 | 182 | it2 = Cmag.find(cur.w & 0x7FFFFFFF7FFFFFFFull); 183 | 184 | mpreal tmp = p0 + p1; 185 | p0 /= tmp; 186 | p1 /= tmp; 187 | 188 | mpreal n = mpreal(rand()) / RAND_MAX; 189 | 190 | if(n < p0) { 191 | measurements[cur.p->v] = 0; 192 | cur = cur.p->e[0]; 193 | } else { 194 | measurements[cur.p->v] = 1; 195 | cur = cur.p->e[2]; 196 | } 197 | } 198 | 199 | if(reset_state) { 200 | QMDDdecref(circ.e); 201 | 202 | QMDDedge e = QMDDone; 203 | QMDDedge edges[4]; 204 | edges[1]=edges[3]=QMDDzero; 205 | 206 | for(int p=0;p()); 220 | } 221 | probs.clear(); 222 | 223 | measurement_done = true; 224 | } 225 | 226 | int Simulator::MeasureOne(int index) { 227 | 228 | std::pair probs = AssignProbsOne(circ.e, index); 229 | 230 | QMDDedge e = circ.e; 231 | 232 | #if VERBOSE 233 | std::cout << " -- measure qubit " << circ.line[index].variable << ": " << std::flush; 234 | #endif 235 | 236 | mpreal sum = probs.first + probs.second; 237 | mpreal norm_factor; 238 | 239 | if(abs(sum - 1) > epsilon) { 240 | if(sum == 0) { 241 | std::cerr << "ERROR: numerical instabilities led to a 0-vector! Abort simulation!" << std::endl; 242 | exit(1); 243 | } 244 | std::cerr << "WARNING in measurement: numerical instability occurred during simulation: |alpha|^2 + |beta|^2 = " << sum << ", but should be 1!"<< std::endl; 245 | } 246 | 247 | #if VERBOSE 248 | std::cout << "p0 = " << probs.first << ", p1 = " << probs.second << std::flush; 249 | #endif 250 | 251 | mpreal n = (mpreal(rand()) / RAND_MAX); 252 | 253 | line[index] = 2; 254 | 255 | QMDD_matrix measure_m; 256 | measure_m[0][0] = measure_m[0][1] = measure_m[1][0] = measure_m[1][1] = COMPLEX_ZERO; 257 | 258 | int measurement; 259 | 260 | if(n < probs.first/sum) { 261 | #if VERBOSE 262 | std::cout << " -> measure 0" << std::endl; 263 | #endif 264 | measure_m[0][0] = COMPLEX_ONE; 265 | measurement = 0; 266 | norm_factor = probs.first; 267 | } else { 268 | #if VERBOSE 269 | std::cout << " -> measure 1" << std::endl; 270 | #endif 271 | measure_m[1][1] = COMPLEX_ONE; 272 | measurement = 1; 273 | norm_factor = probs.second; 274 | } 275 | 276 | QMDDedge f = QMDDmvlgate(measure_m, circ.n, line); 277 | 278 | line[index] = -1; 279 | 280 | 281 | e = QMDDmultiply(f,e); 282 | QMDDdecref(circ.e); 283 | QMDDincref(e); 284 | circ.e = e; 285 | circ.e.w = e.w = Cmul(e.w, Cmake(sqrt(mpreal(1)/mpreal(norm_factor)), mpreal(0))); 286 | 287 | measurement_done = true; 288 | return measurement; 289 | } 290 | 291 | void Simulator::ResetQubit(int index) { 292 | std::pair probs = AssignProbsOne(circ.e, index); 293 | 294 | QMDDedge e = circ.e; 295 | 296 | #if VERBOSE 297 | std::cout << " -- reset qubit " << circ.line[index].variable << ": " << std::flush; 298 | #endif 299 | 300 | mpreal sum = probs.first + probs.second; 301 | mpreal norm_factor; 302 | 303 | #if VERBOSE 304 | std::cout << "p0 = " << probs.first << ", p1 = " << probs.second << std::flush; 305 | #endif 306 | 307 | 308 | if(abs(sum - 1) > epsilon) { 309 | std::cout << "Numerical error occurred during simulation: |alpha0|^2 + |alpha1|^2 = " << sum << ", but should be 1 before reset!"<< std::endl; 310 | exit(1); 311 | } 312 | 313 | line[index] = 2; 314 | 315 | if(probs.first == 0) { 316 | QMDDedge f = QMDDmvlgate(Nm, circ.n, line); 317 | e = QMDDmultiply(f,e); 318 | QMDDdecref(circ.e); 319 | QMDDincref(e); 320 | circ.e = e; 321 | probs.first = mpreal(1); 322 | } 323 | 324 | 325 | QMDD_matrix measure_m; 326 | measure_m[0][1] = measure_m[1][0] = measure_m[1][1] = COMPLEX_ZERO; 327 | measure_m[0][0] = COMPLEX_ONE; 328 | norm_factor = probs.first; 329 | 330 | QMDDedge f = QMDDmvlgate(measure_m, circ.n, line); 331 | 332 | line[index] = -1; 333 | 334 | e = QMDDmultiply(f,e); 335 | QMDDdecref(circ.e); 336 | QMDDincref(e); 337 | circ.e = e; 338 | circ.e.w = e.w = Cmul(e.w, Cmake(sqrt(mpreal(1)/mpreal(probs.first)), mpreal(0))); 339 | } 340 | 341 | std::pair Simulator::AssignProbsOne(QMDDedge e, int index) { 342 | probs.clear(); 343 | AssignProbs(e); 344 | std::queue q; 345 | mpreal pzero, pone; 346 | pzero = pone = mpreal(0); 347 | mpreal prob; 348 | 349 | probsMone.clear(); 350 | visited_nodes2.clear(); 351 | 352 | visited_nodes2.insert(e.p); 353 | auto it2 = Cmag.find(e.w & 0x7FFFFFFF7FFFFFFFull); 354 | probsMone[e.p] = it2->second * it2->second; 355 | mpreal tmp1; 356 | q.push(e.p); 357 | 358 | while(q.front()->v != index) { 359 | QMDDnodeptr ptr = q.front(); 360 | q.pop(); 361 | mpreal prob = probsMone[ptr]; 362 | 363 | if(ptr->e[0].w != COMPLEX_ZERO) { 364 | auto it2 = Cmag.find(ptr->e[0].w & 0x7FFFFFFF7FFFFFFFull); 365 | tmp1 = prob * it2->second * it2->second; 366 | 367 | if(visited_nodes2.find(ptr->e[0].p) != visited_nodes2.end()) { 368 | probsMone[ptr->e[0].p] = probsMone[ptr->e[0].p] + tmp1; 369 | } else { 370 | probsMone[ptr->e[0].p] = tmp1; 371 | visited_nodes2.insert(ptr->e[0].p); 372 | q.push(ptr->e[0].p); 373 | } 374 | } 375 | 376 | if(ptr->e[2].w != COMPLEX_ZERO) { 377 | auto it2 = Cmag.find(ptr->e[2].w & 0x7FFFFFFF7FFFFFFFull); 378 | tmp1 = prob * it2->second * it2->second; 379 | 380 | if(visited_nodes2.find(ptr->e[2].p) != visited_nodes2.end()) { 381 | probsMone[ptr->e[2].p] = probsMone[ptr->e[2].p] + tmp1; 382 | } else { 383 | probsMone[ptr->e[2].p] = tmp1; 384 | visited_nodes2.insert(ptr->e[2].p); 385 | q.push(ptr->e[2].p); 386 | } 387 | } 388 | } 389 | 390 | while(q.size() != 0) { 391 | QMDDnodeptr ptr = q.front(); 392 | q.pop(); 393 | 394 | if(ptr->e[0].w != COMPLEX_ZERO) { 395 | auto it2 = Cmag.find(ptr->e[0].w & 0x7FFFFFFF7FFFFFFFull); 396 | tmp1 = probsMone[ptr] * probs[ptr->e[0].p] * it2->second * it2->second; 397 | pzero = pzero + tmp1; 398 | } 399 | 400 | if(ptr->e[2].w != COMPLEX_ZERO) { 401 | auto it2 = Cmag.find(ptr->e[2].w & 0x7FFFFFFF7FFFFFFFull); 402 | tmp1 = probsMone[ptr] * probs[ptr->e[2].p] * it2->second * it2->second; 403 | pone = pone + tmp1; 404 | } 405 | } 406 | 407 | 408 | probs.clear(); 409 | probsMone.clear(); 410 | visited_nodes2.clear(); 411 | 412 | return std::make_pair(pzero, pone); 413 | } 414 | 415 | uint64_t Simulator::GetElementOfVector(unsigned long long element) { 416 | QMDDedge e = circ.e; 417 | if(QMDDterminal(e)) { 418 | return 0; 419 | } 420 | uint64_t l = COMPLEX_ONE; 421 | do { 422 | l = Cmul(l, e.w); 423 | //cout << "variable q" << QMDDinvorder[e.p->v] << endl; 424 | unsigned long long tmp = (element >> QMDDinvorder[e.p->v]) & 1; 425 | e = e.p->e[2*tmp]; 426 | //element = element % (int)pow(MAXRADIX, QMDDinvorder[e.p->v]+1); 427 | } while(!QMDDterminal(e)); 428 | l = Cmul(l, e.w); 429 | 430 | return l; 431 | } 432 | 433 | mpreal Simulator::GetProbabilityRec(QMDDedge& e) { 434 | 435 | std::unordered_map::iterator it2; 436 | std::unordered_map::iterator it = probs.find(e.p); 437 | if(it != probs.end()) { 438 | it2 = Cmag.find(e.w & 0x7FFFFFFF7FFFFFFFull); 439 | return it2->second * it2->second * it->second; 440 | } 441 | mpreal sum; 442 | if(QMDDterminal(e)) { 443 | sum = mpreal(1); 444 | } else if(line[e.p->v] == 0) { 445 | sum = GetProbabilityRec(e.p->e[0]); 446 | } else if(line[e.p->v] == 1) { 447 | sum = GetProbabilityRec(e.p->e[2]); 448 | } else { 449 | sum = GetProbabilityRec(e.p->e[0]) + GetProbabilityRec(e.p->e[2]); //+ AssignProbs(e.p->e[1]) + AssignProbs(e.p->e[3]); 450 | } 451 | 452 | probs.insert(std::pair(e.p, sum)); 453 | 454 | it2 = Cmag.find(e.w & 0x7FFFFFFF7FFFFFFFull); 455 | 456 | return it2->second * it2->second * sum; 457 | } 458 | 459 | 460 | mpreal Simulator::GetProbability() { 461 | mpreal result = GetProbabilityRec(circ.e); 462 | probs.clear(); 463 | return result; 464 | } 465 | 466 | void Simulator::ApplyGate(QMDDedge gate) { 467 | gatecount++; 468 | 469 | QMDDedge tmp; 470 | 471 | tmp = QMDDmultiply(gate, circ.e); 472 | QMDDincref(tmp); 473 | QMDDdecref(circ.e); 474 | circ.e = tmp; 475 | 476 | if(!measurement_done) { 477 | QMDDdecref(beforeMeasurement); 478 | beforeMeasurement = circ.e; 479 | QMDDincref(beforeMeasurement); 480 | } 481 | 482 | QMDDgarbageCollect(); 483 | 484 | if(ActiveNodeCount > max_active) { 485 | max_active = ActiveNodeCount; 486 | } 487 | 488 | if(Ctable.size() > complex_limit) { 489 | std::vector v; 490 | v.push_back(circ.e); 491 | v.push_back(beforeMeasurement); 492 | 493 | cleanCtable(v); 494 | v.clear(); 495 | if(complex_limit < 2*Ctable.size()) { 496 | complex_limit *= 2; 497 | } 498 | } 499 | 500 | if(measurement_done) { 501 | intermediate_measurement = true; 502 | } 503 | } 504 | 505 | void Simulator::ApplyGate(QMDD_matrix& m) { 506 | QMDDedge f = QMDDmvlgate(m, circ.n, line); 507 | ApplyGate(f); 508 | } 509 | 510 | void Simulator::ResetBeforeMeasurement() { 511 | QMDDdecref(circ.e); 512 | circ.e = beforeMeasurement; 513 | QMDDincref(circ.e); 514 | } 515 | -------------------------------------------------------------------------------- /src/Simulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | */ 25 | 26 | #ifndef SRC_SIMULATOR_H_ 27 | #define SRC_SIMULATOR_H_ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | #define VERBOSE 0 41 | 42 | 43 | class Simulator { 44 | public: 45 | Simulator(); 46 | virtual void Simulate() = 0; 47 | virtual void Simulate(int shots) = 0; 48 | virtual void Reset(); 49 | int GetGatecount() { 50 | return gatecount; 51 | } 52 | int GetQubits() { 53 | return nqubits; 54 | } 55 | int GetMaxActive() { 56 | return max_active; 57 | } 58 | virtual ~Simulator(); 59 | 60 | protected: 61 | int MeasureOne(int index); 62 | void MeasureAll(bool reset_state=true); 63 | void ApplyGate(QMDD_matrix& m); 64 | void ApplyGate(QMDDedge gate); 65 | void AddVariables(int add, std::string name); 66 | void ResetQubit(int index); 67 | mpreal GetProbability(); 68 | 69 | int line[MAXN]; 70 | int measurements[MAXN]; 71 | unsigned int nqubits = 0; 72 | QMDDrevlibDescription circ; 73 | 74 | bool intermediate_measurement = false; 75 | void ResetBeforeMeasurement(); 76 | 77 | uint64_t GetElementOfVector(unsigned long long element); 78 | private: 79 | 80 | mpreal GetProbabilityRec(QMDDedge& e); 81 | QMDDedge AddVariablesRec(QMDDedge e, QMDDedge t, int add); 82 | mpreal AssignProbs(QMDDedge& e); 83 | std::pair AssignProbsOne(QMDDedge e, int index); 84 | 85 | std::unordered_map probs; 86 | std::map probsMone; 87 | std::set visited_nodes2; 88 | std::map dag_edges; 89 | 90 | int max_active = 0; 91 | unsigned int complex_limit = 10000; 92 | int gatecount = 0; 93 | int max_gates = 0x7FFFFFFF; 94 | 95 | bool measurement_done = false; 96 | mpreal epsilon; 97 | QMDDedge beforeMeasurement; 98 | }; 99 | 100 | #endif /* SRC_SIMULATOR_H_ */ 101 | -------------------------------------------------------------------------------- /src/external.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** This header must not contain header guards (like must not). 3 | ** Each time it is invoked, it redefines the macros EXTERN, INITIALIZE 4 | ** based on whether macro DEFINE_VARIABLES is currently defined. 5 | */ 6 | #undef EXTERN 7 | #undef INITIALIZE 8 | 9 | #ifdef DEFINE_VARIABLES 10 | #define EXTERN /* nothing */ 11 | #define INITIALIZE(...) = __VA_ARGS__ 12 | #else 13 | #define EXTERN extern 14 | #define INITIALIZE(...) /* nothing */ 15 | #endif /* DEFINE_VARIABLES */ -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DD-based simulator by JKU Linz, Austria 3 | 4 | Developer: Alwin Zulehner, Robert Wille 5 | 6 | With code from the QMDD implementation provided by Michael Miller (University of Victoria, Canada) 7 | and Philipp Niemann (University of Bremen, Germany). 8 | 9 | For more information, please visit http://iic.jku.at/eda/research/quantum_simulation 10 | 11 | If you have any questions feel free to contact us using 12 | alwin.zulehner@jku.at or robert.wille@jku.at 13 | 14 | If you use the quantum simulator for your research, we would be thankful if you referred to it 15 | by citing the following publication: 16 | 17 | @article{zulehner2018simulation, 18 | title={Advanced Simulation of Quantum Computations}, 19 | author={Zulehner, Alwin and Wille, Robert}, 20 | journal={IEEE Transactions on Computer Aided Design of Integrated Circuits and Systems (TCAD)}, 21 | year={2018}, 22 | eprint = {arXiv:1707.00865} 23 | } 24 | 25 | 26 | Boost Software License - Version 1.0 - August 17th, 2003 27 | 28 | Permission is hereby granted, free of charge, to any person or organization 29 | obtaining a copy of the software and accompanying documentation covered by 30 | this license (the "Software") to use, reproduce, display, distribute, 31 | execute, and transmit the Software, and to prepare derivative works of the 32 | Software, and to permit third-parties to whom the Software is furnished to 33 | do so, all subject to the following: 34 | 35 | The copyright notices in the Software and this entire statement, including 36 | the above license grant, this restriction and the following disclaimer, 37 | must be included in all copies of the Software, in whole or in part, and 38 | all derivative works of the Software, unless such copies or derivative 39 | works are solely in the form of machine-executable object code generated by 40 | a source language processor. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 45 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 46 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 47 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 48 | DEALINGS IN THE SOFTWARE. 49 | 50 | */ 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | #include 68 | //#include 69 | 70 | using mpfr::mpreal; 71 | 72 | using namespace std; 73 | 74 | 75 | int main(int argc, char** argv) { 76 | 77 | namespace po = boost::program_options; 78 | po::options_description description("Allowed options"); 79 | description.add_options() 80 | ("help", "produce help message") 81 | ("seed", po::value(), "seed for random number generator") 82 | ("simulate_qasm", po::value()->implicit_value(""), "simulate a quantum circuit given in QPENQASM 2.0 format (if no file is given, the circuit is read from stdin)") 83 | ("shots", po::value(), "number of shots") 84 | ("ps", "print simulation stats (applied gates, sim. time, and maximal size of the DD)") 85 | ("display_statevector", "adds the state-vector to snapshots") 86 | ("display_probabilities", "adds the probabilities of the basis states to snapshots") 87 | ("precision", po::value(), "two numbers are treated to be equal if their difference is smaller than this value") 88 | ; 89 | 90 | po::variables_map vm; 91 | po::store(po::parse_command_line(argc, argv, description), vm); 92 | po::notify(vm); 93 | 94 | if (vm.count("help")) { 95 | cout << description << "\n"; 96 | return 1; 97 | } 98 | 99 | unsigned long seed = time(NULL); 100 | if (vm.count("seed")) { 101 | seed = vm["seed"].as(); 102 | } 103 | 104 | srand(seed); 105 | 106 | QMDDinit(0); 107 | 108 | if (vm.count("precision")) { 109 | #if VERBOSE 110 | std::cout << "Set precision to " << vm["precision"].as() << std::endl; 111 | #endif 112 | Ctol = mpreal(vm["precision"].as()); 113 | } 114 | 115 | Simulator* simulator; 116 | 117 | if (vm.count("simulate_qasm")) { 118 | string fname = vm["simulate_qasm"].as(); 119 | if(fname == "") { 120 | simulator = new QASMsimulator(vm.count("display_statevector"), vm.count("display_probabilities")); 121 | } else { 122 | simulator = new QASMsimulator(fname, vm.count("display_statevector"), vm.count("display_probabilities")); 123 | } 124 | } else { 125 | cout << description << "\n"; 126 | return 1; 127 | } 128 | 129 | auto t1 = chrono::high_resolution_clock::now(); 130 | 131 | if(vm.count("shots")) { 132 | simulator->Simulate(vm["shots"].as()); 133 | } else { 134 | simulator->Simulate(1); 135 | } 136 | 137 | auto t2 = chrono::high_resolution_clock::now(); 138 | chrono::duration diff = t2-t1; 139 | 140 | delete simulator; 141 | 142 | if (vm.count("ps")) { 143 | cout << endl << "SIMULATION STATS: " << endl; 144 | cout << " Number of applied gates: " << simulator->GetGatecount() << endl; 145 | cout << " Simulation time: " << diff.count() << " seconds" << endl; 146 | cout << " Maximal size of DD (number of nodes) during simulation: " << simulator->GetMaxActive() << endl; 147 | } 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /src/qcost.cpp: -------------------------------------------------------------------------------- 1 | //************************************************************************* 2 | // Computes gate cost based on the cost table on 3 | // Dmitri Maslov's page http://webhome.cs.uvic.ca/~dmaslov/definitions.html 4 | // matches the cost function in QUIVER 5 | // 6 | #include "qcost.h" 7 | 8 | int gate_qcost(int size, int n,int kind) 9 | { 10 | int avail,cost; 11 | 12 | if(kind==PERES_GATE||kind==INV_PERES_GATE) return(4); 13 | avail=n-size+ANCILLARY; 14 | if(size==1) cost=1; 15 | else if(size==2) cost=1; 16 | else if(size==3) cost=5; 17 | else if(size==4) cost=13; 18 | else if(size==5) 19 | { 20 | if(avail>=2) cost=26; 21 | else cost=29; 22 | } 23 | else if(size==6) 24 | { 25 | if(avail>=3) cost=38; 26 | else if(avail>=1) cost=52; 27 | else cost=61; 28 | } 29 | else if(size==7) 30 | { 31 | if(avail>=4) cost=50; 32 | else if(avail>=1) cost=80; 33 | else cost = 125; 34 | } 35 | else if(size==8) 36 | { 37 | if(avail>=5) cost=62; 38 | else if(avail>=1) cost=100; 39 | else cost=253; 40 | } 41 | else if(size==9) 42 | { 43 | if(avail>=6) cost=74; 44 | else if(avail>=1) cost=128; 45 | else cost=509; 46 | } 47 | else if(size==10) 48 | { 49 | if(avail>=7) cost=86; 50 | else if(avail>=1) cost=152; 51 | else cost=1021; 52 | } 53 | else /* size>10 */ 54 | { 55 | if(avail>=size-3) cost=12*size-34; 56 | else if(avail>=1) cost=24*size-88; 57 | else cost=(1<='a'&&ch<='z') ch=ch-'a'+'A'; // convert lowercase letters to uppercase 47 | return(ch); 48 | 49 | } 50 | 51 | char getnbch(FILE *infile) 52 | { 53 | // get next blank character 54 | char ch; 55 | 56 | while(' '==(ch=getch(infile))); 57 | return(ch); 58 | } 59 | 60 | char getstr(FILE *infile,char x[]) 61 | { 62 | // store next token in x (string encapsulated by {","," ","\n"}), return last character of token 63 | char ch; 64 | int i; 65 | do 66 | { 67 | ch=getch(infile); 68 | } while(ch==','||ch==' '||ch=='\n'); 69 | i=0; 70 | while(ch!=','&&ch!=' '&&ch!='\n') 71 | { 72 | x[i]=ch; 73 | i++; 74 | ch=getch(infile); 75 | } 76 | x[i]=0; 77 | return(ch); 78 | } 79 | 80 | int getstr(const char line[],char x[]) 81 | { 82 | // store next token in x (string encapsulated by {","," ","\n"}), return last character of token 83 | char ch; 84 | int i,j = 0; 85 | do 86 | { 87 | ch=processChar(line[j++]); 88 | } while(ch==','||ch==' '||ch=='\n'); 89 | i=0; 90 | while(ch!=','&&ch!=' '&&ch!='\n') 91 | { 92 | x[i]=ch; 93 | i++; 94 | ch=processChar(line[j++]); 95 | } 96 | x[i]=0; 97 | return(j); 98 | } 99 | 100 | int getint(FILE *infile) 101 | { 102 | // read integer from stream 103 | char ch; 104 | int i; 105 | while(' '==(ch=getch(infile))); 106 | i=0; 107 | while(ch!=','&&ch!=' '&&ch!='\n') 108 | { 109 | i=i*10+ch-'0'; 110 | ch=getch(infile); 111 | } 112 | return(i); 113 | } 114 | 115 | void skip2eof(FILE *infile) 116 | { 117 | // skips until it reaches end of file 118 | char ch; 119 | while(EOF!=fscanf(infile,"%c",&ch)); 120 | } 121 | 122 | void skip2eol(FILE *infile) 123 | { 124 | // skips until it reads a '\n' 125 | char ch; 126 | do{ 127 | ch=getch(infile); 128 | if(feof(infile)) return; 129 | } while(ch!='\n'); 130 | } 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/textFileUtilities.h: -------------------------------------------------------------------------------- 1 | #ifndef textFileUtilities_H 2 | #define textFileUtilities_H 3 | 4 | #include 5 | 6 | FILE *openTextFile(char*,char); 7 | char getch(FILE*); 8 | char getnbch(FILE*); 9 | char getstr(FILE*,char*); 10 | int getstr(const char*,char*); 11 | char getline(FILE*,char*); 12 | char processChar(char); 13 | int getint(FILE*); 14 | void skip2eof(FILE*); 15 | void skip2eol(FILE*); 16 | #endif 17 | -------------------------------------------------------------------------------- /src/timing.cpp: -------------------------------------------------------------------------------- 1 | #include "timing.h" 2 | 3 | /************************************************* 4 | These routines can be used for approximate CPU 5 | timing on a WIN system. DMM Sept 2002 6 | ************************************************/ 7 | 8 | long 9 | cpuTime() 10 | /* returns elapsed user CPU time */ 11 | { 12 | return(clock()); 13 | } 14 | 15 | void 16 | printCPUtime(clock_t x) 17 | /* display a time value in sec. */ 18 | { 19 | printf("%7.2f",(float)x/CLOCKS_PER_SEC); 20 | } 21 | 22 | void 23 | printCPUtime(clock_t x, std::ostream &os) 24 | /* display a time value in sec. */ 25 | { 26 | char outputbuffer[30]; 27 | sprintf(outputbuffer, "%7.2f",(float)x/CLOCKS_PER_SEC); 28 | os << outputbuffer; 29 | } 30 | 31 | void 32 | dateToday(char *str) 33 | { 34 | time_t tt; 35 | struct tm *tod; 36 | time(&tt); 37 | tod=localtime(&tt); 38 | sprintf(str,"%2.2d/%2.2d/%d",tod->tm_mday,tod->tm_mon +1,tod->tm_year+1900); 39 | } 40 | 41 | void 42 | wallTime(char *str) 43 | { 44 | time_t tt; 45 | struct tm *tod; 46 | time(&tt); 47 | tod=localtime(&tt); 48 | sprintf(str,"%2.2d:%2.2d:%2.2d",tod->tm_hour,tod->tm_min,tod->tm_sec); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/timing.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMING_H 2 | #define TIMING_H 3 | 4 | //#include 5 | #include 6 | #include 7 | #include 8 | 9 | long cpuTime(); 10 | void printCPUtime(clock_t); /* display a time value in sec. */ 11 | void printCPUtime(clock_t, std::ostream&); /* display a time value in sec. */ 12 | void dateToday(char*); 13 | void wallTime(char*); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """JKUProvider unit tests.""" 9 | -------------------------------------------------------------------------------- /test/_random_circuit_generator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2017, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Generate random circuits.""" 9 | 10 | import random 11 | 12 | import numpy 13 | 14 | from qiskit import (qasm, ClassicalRegister, QuantumCircuit, 15 | QuantumRegister) 16 | 17 | 18 | def choices(population, weights=None, k=1): 19 | """ 20 | Replacement for `random.choices()`, which is only available in Python 3.6+. 21 | TODO: drop once Python 3.6 is required by the sdk. 22 | """ 23 | if weights and sum(weights) != 1: 24 | # Normalize the weights if needed, as numpy.random.choice requires so. 25 | weights = [float(i)/sum(weights) for i in weights] 26 | 27 | return numpy.random.choice(population, size=k, p=weights) 28 | 29 | 30 | class RandomCircuitGenerator(object): 31 | """ 32 | Generate random size circuits for profiling. 33 | """ 34 | def __init__(self, seed=None, 35 | max_qubits=5, min_qubits=1, 36 | max_depth=100, min_depth=1): 37 | """ 38 | Args: 39 | seed (int): Random number seed. If none, don't seed the generator. 40 | max_qubits (int): Maximum number of qubits in a circuit. 41 | min_qubits (int): Minimum number of operations in a cirucit. 42 | max_depth (int): Maximum number of operations in a circuit. 43 | min_depth (int): Minimum number of operations in circuit. 44 | """ 45 | self.max_depth = max_depth 46 | self.max_qubits = max_qubits 47 | self.min_depth = min_depth 48 | self.min_qubits = min_qubits 49 | self.circuit_list = [] 50 | self.n_qubit_list = [] 51 | self.depth_list = [] 52 | self.basis_gates = None 53 | self.circuit_name_list = [] 54 | if seed is not None: 55 | random.seed(a=seed) 56 | # specify number of parameters and args for each op 57 | # in the standard extension. If type hints (PEP484) are followed 58 | # maybe we can guess this. "nregs" are the number of qubits the 59 | # operation uses. If nregs=0 then it means either 1 qubit or 60 | # 1 register. "nparams" are the number of parameters the operation takes. 61 | self.op_signature = { 62 | 'barrier': {'nregs': 0, 'nparams': None}, 63 | 'ccx': {'nregs': 3, 'nparams': None}, 64 | 'ch': {'nregs': 2, 'nparams': None}, 65 | 'crz': {'nregs': 2, 'nparams': 1}, 66 | 'cswap': {'nregs': 3, 'nparams': None}, 67 | 'cu1': {'nregs': 2, 'nparams': 1}, 68 | 'cu3': {'nregs': 2, 'nparams': 3}, 69 | 'cx': {'nregs': 2, 'nparams': None}, 70 | 'cy': {'nregs': 2, 'nparams': None}, 71 | 'cz': {'nregs': 2, 'nparams': None}, 72 | 'h': {'nregs': 1, 'nparams': None}, 73 | 'iden': {'nregs': 1, 'nparams': None}, 74 | 'measure': {'nregs': 0, 'nparams': None}, 75 | 'reset': {'nregs': 1, 'nparams': None}, 76 | 'rx': {'nregs': 1, 'nparams': 1}, 77 | 'ry': {'nregs': 1, 'nparams': 1}, 78 | 'rz': {'nregs': 1, 'nparams': 1}, 79 | 's': {'nregs': 1, 'nparams': None}, 80 | 't': {'nregs': 1, 'nparams': None}, 81 | 'u1': {'nregs': 1, 'nparams': 1}, 82 | 'u2': {'nregs': 1, 'nparams': 2}, 83 | 'u3': {'nregs': 1, 'nparams': 3}, 84 | 'x': {'nregs': 1, 'nparams': None}, 85 | 'y': {'nregs': 1, 'nparams': None}, 86 | 'z': {'nregs': 1, 'nparams': None}} 87 | 88 | def add_circuits(self, n_circuits, do_measure=True, basis=None, 89 | basis_weights=None): 90 | """Adds circuits to program. 91 | 92 | Generates a circuit with a random number of operations in `basis`. 93 | Also adds a random number of measurements in 94 | [1,nQubits] to end of circuit. 95 | 96 | Args: 97 | n_circuits (int): Number of circuits to add. 98 | do_measure (bool): Whether to add measurements. 99 | basis (list(str) or None): List of op names. If None, basis 100 | is randomly chosen with unique ops in (2,7) 101 | basis_weights (list(float) or None): List of weights 102 | corresponding to indices in `basis`. 103 | Raises: 104 | AttributeError: if operation is not recognized. 105 | """ 106 | if basis is None: 107 | basis = list(random.sample(self.op_signature.keys(), 108 | random.randint(2, 7))) 109 | basis_weights = [1./len(basis)] * len(basis) 110 | if basis_weights is not None: 111 | assert len(basis) == len(basis_weights) 112 | uop_basis = basis[:] 113 | if basis_weights: 114 | uop_basis_weights = basis_weights[:] 115 | else: 116 | uop_basis_weights = None 117 | # remove barrier from uop basis if it is specified 118 | if 'barrier' in uop_basis: 119 | ind = uop_basis.index('barrier') 120 | del uop_basis[ind] 121 | if uop_basis_weights: 122 | del uop_basis_weights[ind] 123 | # remove measure from uop basis if it is specified 124 | if 'measure' in uop_basis: 125 | ind = uop_basis.index('measure') 126 | del uop_basis[ind] 127 | if uop_basis_weights: 128 | del uop_basis_weights[ind] 129 | # self.basis_gates = uop_basis 130 | self.basis_gates = basis 131 | self.circuit_name_list = [] 132 | # TODO: replace choices with random.choices() when python 3.6 is 133 | # required. 134 | self.n_qubit_list = choices( 135 | range(self.min_qubits, self.max_qubits + 1), k=n_circuits) 136 | self.depth_list = choices( 137 | range(self.min_depth, self.max_depth + 1), k=n_circuits) 138 | for i_circuit in range(n_circuits): 139 | n_qubits = self.n_qubit_list[i_circuit] 140 | if self.min_regs_exceeds_nqubits(uop_basis, n_qubits): 141 | # no gate operation from this circuit can fit in the available 142 | # number of qubits. 143 | continue 144 | depth_cnt = self.depth_list[i_circuit] 145 | reg_pop = numpy.arange(1, n_qubits+1) 146 | register_weights = reg_pop[::-1].astype(float) 147 | register_weights /= register_weights.sum() 148 | max_registers = numpy.random.choice(reg_pop, p=register_weights) 149 | reg_weight = numpy.ones(max_registers) / float(max_registers) 150 | reg_sizes = rand_register_sizes(n_qubits, reg_weight) 151 | n_registers = len(reg_sizes) 152 | circuit = QuantumCircuit() 153 | for i_size, size in enumerate(reg_sizes): 154 | cr_name = 'cr' + str(i_size) 155 | qr_name = 'qr' + str(i_size) 156 | creg = ClassicalRegister(int(size), cr_name) 157 | qreg = QuantumRegister(int(size), qr_name) 158 | circuit.add_register(qreg, creg) 159 | while depth_cnt > 0: 160 | # TODO: replace choices with random.choices() when python 3.6 161 | # is required. 162 | op_name = choices(basis, weights=basis_weights)[0] 163 | if hasattr(circuit, op_name): 164 | operator = getattr(circuit, op_name) 165 | else: 166 | raise AttributeError('operation \"{0}\"' 167 | ' not recognized'.format(op_name)) 168 | n_regs = self.op_signature[op_name]['nregs'] 169 | n_params = self.op_signature[op_name]['nparams'] 170 | if n_regs == 0: # this is a barrier or measure 171 | n_regs = random.randint(1, n_qubits) 172 | if n_qubits >= n_regs: 173 | # warning: assumes op function signature specifies 174 | # op parameters before qubits 175 | op_args = [] 176 | if n_params: 177 | op_args = [random.random() for _ in range(n_params)] 178 | if op_name == 'measure': 179 | # if measure occurs here, assume it's to do a conditional 180 | # randomly select a register to measure 181 | ireg = random.randint(0, n_registers-1) 182 | qr_name = 'qr' + str(ireg) 183 | cr_name = 'cr' + str(ireg) 184 | qreg = circuit.regs[qr_name] 185 | creg = circuit.regs[cr_name] 186 | for qind in range(qreg.size): 187 | operator(qreg[qind], creg[qind]) 188 | ifval = random.randint(0, (1 << qreg.size) - 1) 189 | # TODO: replace choices with random.choices() when 190 | # python 3.6 is required. 191 | uop_name = choices(uop_basis, weights=uop_basis_weights)[0] 192 | if hasattr(circuit, uop_name): 193 | uop = getattr(circuit, uop_name) 194 | else: 195 | raise AttributeError('operation \"{0}\"' 196 | ' not recognized'.format(uop_name)) 197 | unregs = self.op_signature[uop_name]['nregs'] 198 | unparams = self.op_signature[uop_name]['nparams'] 199 | if unregs == 0: # this is a barrier or measure 200 | unregs = random.randint(1, n_qubits) 201 | if qreg.size >= unregs: 202 | qind_list = random.sample(range(qreg.size), unregs) 203 | uop_args = [] 204 | if unparams: 205 | uop_args = [random.random() for _ in range(unparams)] 206 | uop_args.extend([qreg[qind] for qind in qind_list]) 207 | uop(*uop_args).c_if(creg, ifval) 208 | depth_cnt -= 1 209 | elif op_name == 'barrier': 210 | ireg = random.randint(0, n_registers-1) 211 | qreg = circuit.qregs[ireg] 212 | bar_args = [(qreg, mi) for mi in range(qreg.size)] 213 | operator(*bar_args) 214 | else: 215 | # select random register 216 | ireg = random.randint(0, n_registers-1) 217 | qreg = circuit.qregs[ireg] 218 | if qreg.size >= n_regs: 219 | qind_list = random.sample(range(qreg.size), n_regs) 220 | op_args.extend([qreg[qind] for qind in qind_list]) 221 | operator(*op_args) 222 | depth_cnt -= 1 223 | else: 224 | break 225 | nmeasure = random.randint(1, n_qubits) 226 | m_list = random.sample(range(nmeasure), nmeasure) 227 | if do_measure: 228 | for qind in m_list: 229 | rind = 0 # register index 230 | cumtot = 0 231 | while qind >= cumtot + circuit.qregs[rind].size: 232 | cumtot += circuit.qregs[rind].size 233 | rind += 1 234 | qrind = int(qind - cumtot) 235 | qreg = circuit.qregs[rind] 236 | creg = circuit.cregs[rind] 237 | circuit.measure(qreg[qrind], creg[qrind]) 238 | self.circuit_list.append(circuit) 239 | 240 | def min_regs_exceeds_nqubits(self, basis, n_qubits): 241 | """Check whether the minimum number of qubits used by the operations 242 | in basis is between 1 and the number of qubits. 243 | 244 | Args: 245 | basis (list): list of basis names 246 | n_qubits (int): number of qubits in circuit 247 | Returns: 248 | boolean: result of the check. 249 | """ 250 | return not any((n_qubits >= self.op_signature[opName]['nregs'] > 0 251 | for opName in basis)) 252 | 253 | def get_circuits(self, format_='QuantumCircuit'): 254 | """Get the compiled circuits generated. 255 | 256 | Args: 257 | format_ (str, optional): "qasm" | "qobj" | "QuantumCircuit" 258 | 259 | Returns: 260 | list: List of Compiled QuantumCircuit objects. 261 | 262 | Raises: 263 | NameError: if the output format is not valid. 264 | """ 265 | if format_ == 'qasm': 266 | qasm_list = [] 267 | for circuit in self.circuit_list: 268 | qasm_list.append(circuit.qasm()) 269 | return qasm_list 270 | elif format_ == 'QuantumCircuit': 271 | qc_list = [] 272 | for circuit in self.circuit_list: 273 | qc_list.append(circuit) 274 | return qc_list 275 | else: 276 | raise NameError('Unrecognized circuit output format: "{}"'.format( 277 | format_)) 278 | 279 | 280 | def rand_register_sizes(n_registers, pvals): 281 | """Return a randomly chosen list of nRegisters summing to nQubits.""" 282 | vector = numpy.random.multinomial(n_registers, pvals) 283 | return vector[vector.nonzero()] 284 | -------------------------------------------------------------------------------- /test/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2017, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Shared functionality and helpers for the unit tests.""" 9 | 10 | from enum import Enum 11 | import functools 12 | import inspect 13 | import logging 14 | import os 15 | import unittest 16 | from unittest.util import safe_repr 17 | from qiskit import __path__ as qiskit_path 18 | 19 | 20 | class Path(Enum): 21 | """Helper with paths commonly used during the tests.""" 22 | # Main SDK path: qiskit/ 23 | SDK = qiskit_path[0] 24 | # test.python path: qiskit/test/python/ 25 | TEST = os.path.dirname(__file__) 26 | # Examples path: examples/ 27 | EXAMPLES = os.path.join(SDK, '../examples') 28 | # Schemas path: qiskit/schemas 29 | SCHEMAS = os.path.join(SDK, 'schemas') 30 | 31 | 32 | class QiskitTestCase(unittest.TestCase): 33 | """Helper class that contains common functionality.""" 34 | 35 | @classmethod 36 | def setUpClass(cls): 37 | cls.moduleName = os.path.splitext(inspect.getfile(cls))[0] 38 | cls.log = logging.getLogger(cls.__name__) 39 | 40 | # Set logging to file and stdout if the LOG_LEVEL environment variable 41 | # is set. 42 | if os.getenv('LOG_LEVEL'): 43 | # Set up formatter. 44 | log_fmt = ('{}.%(funcName)s:%(levelname)s:%(asctime)s:' 45 | ' %(message)s'.format(cls.__name__)) 46 | formatter = logging.Formatter(log_fmt) 47 | 48 | # Set up the file handler. 49 | log_file_name = '%s.log' % cls.moduleName 50 | file_handler = logging.FileHandler(log_file_name) 51 | file_handler.setFormatter(formatter) 52 | cls.log.addHandler(file_handler) 53 | 54 | # Set the logging level from the environment variable, defaulting 55 | # to INFO if it is not a valid level. 56 | level = logging._nameToLevel.get(os.getenv('LOG_LEVEL'), 57 | logging.INFO) 58 | cls.log.setLevel(level) 59 | 60 | @staticmethod 61 | def _get_resource_path(filename, path=Path.TEST): 62 | """ Get the absolute path to a resource. 63 | 64 | Args: 65 | filename (string): filename or relative path to the resource. 66 | path (Path): path used as relative to the filename. 67 | Returns: 68 | str: the absolute path to the resource. 69 | """ 70 | return os.path.normpath(os.path.join(path.value, filename)) 71 | 72 | def assertNoLogs(self, logger=None, level=None): 73 | """ 74 | Context manager to test that no message is sent to the specified 75 | logger and level (the opposite of TestCase.assertLogs()). 76 | """ 77 | # pylint: disable=invalid-name 78 | return _AssertNoLogsContext(self, logger, level) 79 | 80 | def assertDictAlmostEqual(self, dict1, dict2, delta=None, msg=None, 81 | places=None, default_value=0): 82 | """ 83 | Assert two dictionaries with numeric values are almost equal. 84 | 85 | Fail if the two dictionaries are unequal as determined by 86 | comparing that the difference between values with the same key are 87 | not greater than delta (default 1e-8), or that difference rounded 88 | to the given number of decimal places is not zero. If a key in one 89 | dictionary is not in the other the default_value keyword argument 90 | will be used for the missing value (default 0). If the two objects 91 | compare equal then they will automatically compare almost equal. 92 | 93 | Args: 94 | dict1 (dict): a dictionary. 95 | dict2 (dict): a dictionary. 96 | delta (number): threshold for comparison (defaults to 1e-8). 97 | msg (str): return a custom message on failure. 98 | places (int): number of decimal places for comparison. 99 | default_value (number): default value for missing keys. 100 | 101 | Raises: 102 | TypeError: raises TestCase failureException if the test fails. 103 | """ 104 | # pylint: disable=invalid-name 105 | if dict1 == dict2: 106 | # Shortcut 107 | return 108 | if delta is not None and places is not None: 109 | raise TypeError("specify delta or places not both") 110 | 111 | if places is not None: 112 | success = True 113 | standard_msg = '' 114 | # check value for keys in target 115 | keys1 = set(dict1.keys()) 116 | for key in keys1: 117 | val1 = dict1.get(key, default_value) 118 | val2 = dict2.get(key, default_value) 119 | if round(abs(val1 - val2), places) != 0: 120 | success = False 121 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 122 | safe_repr(val1), 123 | safe_repr(val2)) 124 | # check values for keys in counts, not in target 125 | keys2 = set(dict2.keys()) - keys1 126 | for key in keys2: 127 | val1 = dict1.get(key, default_value) 128 | val2 = dict2.get(key, default_value) 129 | if round(abs(val1 - val2), places) != 0: 130 | success = False 131 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 132 | safe_repr(val1), 133 | safe_repr(val2)) 134 | if success is True: 135 | return 136 | standard_msg = standard_msg[:-2] + ' within %s places' % places 137 | 138 | else: 139 | if delta is None: 140 | delta = 1e-8 # default delta value 141 | success = True 142 | standard_msg = '' 143 | # check value for keys in target 144 | keys1 = set(dict1.keys()) 145 | for key in keys1: 146 | val1 = dict1.get(key, default_value) 147 | val2 = dict2.get(key, default_value) 148 | if abs(val1 - val2) > delta: 149 | success = False 150 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 151 | safe_repr(val1), 152 | safe_repr(val2)) 153 | # check values for keys in counts, not in target 154 | keys2 = set(dict2.keys()) - keys1 155 | for key in keys2: 156 | val1 = dict1.get(key, default_value) 157 | val2 = dict2.get(key, default_value) 158 | if abs(val1 - val2) > delta: 159 | success = False 160 | standard_msg += '(%s: %s != %s), ' % (safe_repr(key), 161 | safe_repr(val1), 162 | safe_repr(val2)) 163 | if success is True: 164 | return 165 | standard_msg = standard_msg[:-2] + ' within %s delta' % delta 166 | 167 | msg = self._formatMessage(msg, standard_msg) 168 | raise self.failureException(msg) 169 | 170 | 171 | class _AssertNoLogsContext(unittest.case._AssertLogsContext): 172 | """A context manager used to implement TestCase.assertNoLogs().""" 173 | 174 | # pylint: disable=inconsistent-return-statements 175 | def __exit__(self, exc_type, exc_value, tb): 176 | """ 177 | This is a modified version of TestCase._AssertLogsContext.__exit__(...) 178 | """ 179 | self.logger.handlers = self.old_handlers 180 | self.logger.propagate = self.old_propagate 181 | self.logger.setLevel(self.old_level) 182 | if exc_type is not None: 183 | # let unexpected exceptions pass through 184 | return False 185 | 186 | if self.watcher.records: 187 | msg = 'logs of level {} or higher triggered on {}:\n'.format( 188 | logging.getLevelName(self.level), self.logger.name) 189 | for record in self.watcher.records: 190 | msg += 'logger %s %s:%i: %s\n' % (record.name, record.pathname, 191 | record.lineno, 192 | record.getMessage()) 193 | 194 | self._raiseFailure(msg) 195 | 196 | 197 | def slow_test(func): 198 | """ 199 | Decorator that signals that the test takes minutes to run. 200 | 201 | Args: 202 | func (callable): test function to be decorated. 203 | 204 | Returns: 205 | callable: the decorated function. 206 | """ 207 | 208 | @functools.wraps(func) 209 | def _(*args, **kwargs): 210 | if SKIP_SLOW_TESTS: 211 | raise unittest.SkipTest('Skipping slow tests') 212 | return func(*args, **kwargs) 213 | 214 | return _ 215 | 216 | 217 | def requires_qe_access(func): 218 | """ 219 | Decorator that signals that the test uses the online API: 220 | * determines if the test should be skipped by checking environment 221 | variables. 222 | * if the test is not skipped, it reads `QE_TOKEN` and `QE_URL` from 223 | `Qconfig.py` or from environment variables. 224 | * if the test is not skipped, it appends `QE_TOKEN` and `QE_URL` as 225 | arguments to the test function. 226 | Args: 227 | func (callable): test function to be decorated. 228 | 229 | Returns: 230 | callable: the decorated function. 231 | """ 232 | 233 | @functools.wraps(func) 234 | def _(*args, **kwargs): 235 | # pylint: disable=invalid-name 236 | if SKIP_ONLINE_TESTS: 237 | raise unittest.SkipTest('Skipping online tests') 238 | 239 | # Try to read the variables from Qconfig. 240 | try: 241 | import Qconfig 242 | QE_TOKEN = Qconfig.APItoken 243 | QE_URL = Qconfig.config['url'] 244 | QE_HUB = Qconfig.config.get('hub') 245 | QE_GROUP = Qconfig.config.get('group') 246 | QE_PROJECT = Qconfig.config.get('project') 247 | except ImportError: 248 | # Try to read them from environment variables (ie. Travis). 249 | QE_TOKEN = os.getenv('QE_TOKEN') 250 | QE_URL = os.getenv('QE_URL') 251 | QE_HUB = os.getenv('QE_HUB') 252 | QE_GROUP = os.getenv('QE_GROUP') 253 | QE_PROJECT = os.getenv('QE_PROJECT') 254 | if not QE_TOKEN or not QE_URL: 255 | raise Exception( 256 | 'Could not locate a valid "Qconfig.py" file nor read the QE ' 257 | 'values from the environment') 258 | 259 | kwargs['QE_TOKEN'] = QE_TOKEN 260 | kwargs['QE_URL'] = QE_URL 261 | kwargs['hub'] = QE_HUB 262 | kwargs['group'] = QE_GROUP 263 | kwargs['project'] = QE_PROJECT 264 | return func(*args, **kwargs) 265 | 266 | return _ 267 | 268 | 269 | def _is_ci_fork_pull_request(): 270 | """ 271 | Check if the tests are being run in a CI environment and if it is a pull 272 | request. 273 | 274 | Returns: 275 | bool: True if the tests are executed inside a CI tool, and the changes 276 | are not against the "master" branch. 277 | """ 278 | if os.getenv('TRAVIS'): 279 | # Using Travis CI. 280 | if os.getenv('TRAVIS_PULL_REQUEST_BRANCH'): 281 | return True 282 | elif os.getenv('APPVEYOR'): 283 | # Using AppVeyor CI. 284 | if os.getenv('APPVEYOR_PULL_REQUEST_NUMBER'): 285 | return True 286 | return False 287 | 288 | 289 | SKIP_ONLINE_TESTS = os.getenv('SKIP_ONLINE_TESTS', _is_ci_fork_pull_request()) 290 | SKIP_SLOW_TESTS = os.getenv('SKIP_SLOW_TESTS', True) not in ['false', 'False', '-1'] 291 | -------------------------------------------------------------------------------- /test/qasms/example.qasm: -------------------------------------------------------------------------------- 1 | OPENQASM 2.0; 2 | include "qelib1.inc"; 3 | qreg q[3]; 4 | qreg r[3]; 5 | h q; 6 | cx q, r; 7 | creg c[3]; 8 | creg d[3]; 9 | barrier q; 10 | measure q->c; 11 | measure r->d; 12 | -------------------------------------------------------------------------------- /test/test_jku_backend.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2019, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Test JKU backend.""" 9 | 10 | from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister 11 | from qiskit import execute 12 | from qiskit_jku_provider import QasmSimulator 13 | 14 | from .common import QiskitTestCase 15 | 16 | 17 | class JKUBackendTestCase(QiskitTestCase): 18 | """Tests for the JKU backend.""" 19 | 20 | def setUp(self): 21 | super().setUp() 22 | self.backend = QasmSimulator(silent=True) 23 | 24 | def test_configuration(self): 25 | """Test backend.configuration().""" 26 | configuration = self.backend.configuration() 27 | return configuration 28 | 29 | def test_properties(self): 30 | """Test backend.properties().""" 31 | properties = self.backend.properties() 32 | self.assertEqual(properties, None) 33 | 34 | def test_status(self): 35 | """Test backend.status().""" 36 | status = self.backend.status() 37 | return status 38 | 39 | def test_run_circuit(self): 40 | """Test running a single circuit.""" 41 | result = execute(bell(), self.backend, seed_transpiler=34342).result() 42 | self.assertEqual(result.success, True) 43 | return result 44 | 45 | 46 | def bell(): 47 | """Return a Bell circuit.""" 48 | qr = QuantumRegister(2, name='qr') 49 | cr = ClassicalRegister(2, name='qc') 50 | qc = QuantumCircuit(qr, cr, name='bell') 51 | qc.h(qr[0]) 52 | qc.cx(qr[0], qr[1]) 53 | qc.measure(qr, cr) 54 | return qc 55 | -------------------------------------------------------------------------------- /test/test_jku_provider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2019, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | """Test JKU backend.""" 9 | 10 | from qiskit_jku_provider import JKUProvider 11 | 12 | from .common import QiskitTestCase 13 | 14 | 15 | class JKUProviderTestCase(QiskitTestCase): 16 | """Tests for the JKU Provider.""" 17 | 18 | def setUp(self): 19 | self.provider = JKUProvider() 20 | self.backend_name = 'qasm_simulator' 21 | 22 | def test_backends(self): 23 | """Test the provider has backends.""" 24 | backends = self.provider.backends() 25 | self.assertTrue(len(backends) > 0) 26 | 27 | def test_get_backend(self): 28 | """Test getting a backend from the provider.""" 29 | backend = self.provider.get_backend(name=self.backend_name) 30 | self.assertEqual(backend.name(), self.backend_name) 31 | -------------------------------------------------------------------------------- /test/test_jku_snapshot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | # pylint: disable=missing-docstring,broad-except 9 | 10 | import unittest 11 | from qiskit import QuantumCircuit, QuantumRegister 12 | from qiskit import execute 13 | 14 | from .common import QiskitTestCase 15 | from qiskit_jku_provider import QasmSimulator 16 | 17 | 18 | class JKUSnapshotTest(QiskitTestCase): 19 | """Test JKU's statevector return capatbilities.""" 20 | 21 | def setUp(self): 22 | super().setUp() 23 | self.backend = QasmSimulator(silent=True) 24 | qr = QuantumRegister(2) 25 | self.q_circuit = QuantumCircuit(qr) 26 | self.q_circuit.h(qr[0]) 27 | self.q_circuit.cx(qr[0], qr[1]) 28 | 29 | def test_statevector_output(self): 30 | """Test final state vector for single circuit run.""" 31 | result = execute(self.q_circuit, backend=self.backend).result() 32 | self.assertEqual(result.success, True) 33 | actual = result.get_statevector(self.q_circuit) 34 | 35 | # state is 1/sqrt(2)|00> + 1/sqrt(2)|11>, up to a global phase 36 | self.assertAlmostEqual((abs(actual[0]))**2, 1/2, places=5) 37 | self.assertEqual(actual[1], 0) 38 | self.assertEqual(actual[2], 0) 39 | self.assertAlmostEqual((abs(actual[3]))**2, 1/2, places=5) 40 | 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /test/test_multi_registers_convention.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | # pylint: disable=unused-import 9 | # pylint: disable=redefined-builtin 10 | 11 | """Test Qiskit's QuantumCircuit class for multiple registers.""" 12 | 13 | from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit 14 | from qiskit import execute 15 | from qiskit.quantum_info import state_fidelity, basis_state 16 | from qiskit.test import QiskitTestCase 17 | from qiskit_jku_provider import QasmSimulator 18 | 19 | 20 | class TestCircuitMultiRegs(QiskitTestCase): 21 | """QuantumCircuit Qasm tests.""" 22 | 23 | def test_circuit_multi(self): 24 | """Test circuit multi regs declared at start. 25 | """ 26 | qreg0 = QuantumRegister(2, 'q0') 27 | creg0 = ClassicalRegister(2, 'c0') 28 | qreg1 = QuantumRegister(2, 'q1') 29 | creg1 = ClassicalRegister(2, 'c1') 30 | circ = QuantumCircuit(qreg0, qreg1) 31 | circ.x(qreg0[1]) 32 | circ.x(qreg1[0]) 33 | 34 | meas = QuantumCircuit(qreg0, qreg1, creg0, creg1) 35 | meas.measure(qreg0, creg0) 36 | meas.measure(qreg1, creg1) 37 | 38 | qc = circ + meas 39 | 40 | backend_sim = QasmSimulator(silent=True) 41 | 42 | result = execute(qc, backend_sim, seed_transpiler=34342).result() 43 | counts = result.get_counts(qc) 44 | 45 | target = {'01 10': 1024} 46 | 47 | result = execute(circ, backend_sim, seed_transpiler=3438).result() 48 | state = result.get_statevector(circ) 49 | 50 | self.assertEqual(counts, target) 51 | self.assertAlmostEqual(state_fidelity(basis_state('0110', 4), state), 1.0, places=7) 52 | -------------------------------------------------------------------------------- /test/test_qasm_simulator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2017, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | # pylint: disable=missing-docstring,redefined-builtin 9 | 10 | import unittest 11 | import os 12 | from qiskit import QuantumCircuit 13 | from .common import QiskitTestCase 14 | from qiskit_jku_provider import QasmSimulator 15 | from qiskit import execute 16 | 17 | 18 | class TestQasmSimulatorJKUBasic(QiskitTestCase): 19 | """Runs the Basic qasm_simulator tests from Terra on JKU.""" 20 | 21 | def setUp(self): 22 | self.seed = 88 23 | self.backend = QasmSimulator(silent=True) 24 | qasm_filename = os.path.join(os.path.dirname(__file__), 'qasms', 'example.qasm') 25 | compiled_circuit = QuantumCircuit.from_qasm_file(qasm_filename) 26 | compiled_circuit.name = 'test' 27 | self.circuit = compiled_circuit 28 | 29 | def test_qasm_simulator_single_shot(self): 30 | """Test single shot run.""" 31 | result = execute(self.circuit, self.backend, seed_transpiler=34342, shots=1).result() 32 | self.assertEqual(result.success, True) 33 | 34 | def test_qasm_simulator(self): 35 | """Test data counts output for single circuit run against reference.""" 36 | shots = 1024 37 | result = execute(self.circuit, self.backend, seed_transpiler=34342, shots=shots).result() 38 | threshold = 0.04 * shots 39 | counts = result.get_counts('test') 40 | target = {'100 100': shots / 8, '011 011': shots / 8, 41 | '101 101': shots / 8, '111 111': shots / 8, 42 | '000 000': shots / 8, '010 010': shots / 8, 43 | '110 110': shots / 8, '001 001': shots / 8} 44 | self.assertDictAlmostEqual(counts, target, threshold) 45 | 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /test/test_qasm_simulator_jku.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018, IBM. 4 | # 5 | # This source code is licensed under the Apache License, Version 2.0 found in 6 | # the LICENSE.txt file in the root directory of this source tree. 7 | 8 | import random 9 | import unittest 10 | 11 | import numpy 12 | from scipy.stats import chi2_contingency 13 | 14 | from qiskit import execute 15 | from qiskit import QuantumCircuit 16 | from qiskit import QuantumRegister 17 | from qiskit import ClassicalRegister 18 | from qiskit import BasicAer 19 | from qiskit_jku_provider import QasmSimulator 20 | 21 | try: 22 | global_pq_simulator = QasmSimulator(silent=True) 23 | except ImportError: 24 | _skip_class = True 25 | else: 26 | _skip_class = False 27 | 28 | 29 | from ._random_circuit_generator import RandomCircuitGenerator 30 | from .common import QiskitTestCase 31 | 32 | 33 | @unittest.skipIf(_skip_class, 'JKU C++ simulator unavailable') 34 | class TestQasmSimulatorJKU(QiskitTestCase): 35 | """ 36 | Test JKU simulator. 37 | """ 38 | 39 | # noinspection PyPep8Naming 40 | @classmethod 41 | def setUpClass(cls): 42 | super().setUpClass() 43 | 44 | # Set up random circuits 45 | n_circuits = 20 46 | min_depth = 1 47 | max_depth = 50 48 | min_qubits = 1 49 | max_qubits = 4 50 | random_circuits = RandomCircuitGenerator(min_qubits=min_qubits, 51 | max_qubits=max_qubits, 52 | min_depth=min_depth, 53 | max_depth=max_depth, 54 | seed=None) 55 | for _ in range(n_circuits): 56 | basis = list(random.sample(random_circuits.op_signature.keys(), 57 | random.randint(2, 7))) 58 | if 'reset' in basis: 59 | basis.remove('reset') 60 | if 'u0' in basis: 61 | basis.remove('u0') 62 | if 'measure' in basis: 63 | basis.remove('measure') 64 | random_circuits.add_circuits(1, basis=basis) 65 | cls.rqg = random_circuits 66 | 67 | def run_on_simulators(self, qc, pq_simulator, qk_simulator, shots, seed): 68 | job_pq = execute(qc, pq_simulator, shots=shots, seed_simulator=seed) 69 | job_qk = execute(qc, qk_simulator, shots=shots, seed_simulator=seed) 70 | counts_pq = job_pq.result().get_counts() 71 | counts_qk = job_qk.result().get_counts() 72 | states = counts_qk.keys() | counts_pq.keys() 73 | # contingency table 74 | ctable = numpy.array([[counts_pq.get(key, 0) for key in states], 75 | [counts_qk.get(key, 0) for key in states]]) 76 | result = chi2_contingency(ctable) 77 | return counts_pq, counts_qk, result 78 | 79 | def test_gate_x(self): 80 | shots = 100 81 | qr = QuantumRegister(1) 82 | cr = ClassicalRegister(1) 83 | qc = QuantumCircuit(qr, cr, name='test_gate_x') 84 | qc.x(qr[0]) 85 | qc.measure(qr, cr) 86 | job = execute(qc, global_pq_simulator, shots=shots) 87 | result_pq = job.result(timeout=30) 88 | self.assertEqual(result_pq.get_counts(), 89 | {'1': shots}) 90 | 91 | def test_entangle(self): 92 | shots = 100 93 | N = 5 94 | qr = QuantumRegister(N) 95 | cr = ClassicalRegister(N) 96 | qc = QuantumCircuit(qr, cr, name='test_entangle') 97 | 98 | qc.h(qr[0]) 99 | for i in range(1, N): 100 | qc.cx(qr[0], qr[i]) 101 | qc.measure(qr, cr) 102 | timeout = 30 103 | job = execute(qc, global_pq_simulator, shots=shots) 104 | result = job.result(timeout=timeout) 105 | counts = result.get_counts() 106 | self.log.info(counts) 107 | for key, _ in counts.items(): 108 | with self.subTest(key=key): 109 | self.assertTrue(key in ['0' * N, '1' * N]) 110 | 111 | def test_output_style(self): 112 | qk_simulator = BasicAer.get_backend('qasm_simulator', ) 113 | 114 | qr = QuantumRegister(2) 115 | cr = ClassicalRegister(2) 116 | qc = QuantumCircuit(qr, cr, name='test_output_order') 117 | qc.h(qr[0]) 118 | qc.measure(qr[0], cr[0]) 119 | qc.measure(qr[1], cr[1]) 120 | shots = 100 121 | 122 | counts_pq, counts_qk, result = self.run_on_simulators(qc, global_pq_simulator, 123 | qk_simulator, shots=shots, seed=1) 124 | self.assertGreater(result[1], 0.01) 125 | 126 | cr1 = ClassicalRegister(1) 127 | cr2 = ClassicalRegister(1) 128 | qc = QuantumCircuit(qr, cr1, cr2, name='test_output_separation') 129 | qc.h(qr[0]) 130 | qc.measure(qr[0], cr1[0]) 131 | qc.measure(qr[1], cr2[0]) 132 | 133 | counts_pq, counts_qk, result = self.run_on_simulators(qc, global_pq_simulator, 134 | qk_simulator, shots=shots, seed=1) 135 | self.log.info('chi2_contingency: %s', str(result)) 136 | self.assertGreater(result[1], 0.01) 137 | 138 | def test_random_circuits(self): 139 | qk_simulator = BasicAer.get_backend('qasm_simulator', ) 140 | for circuit in self.rqg.get_circuits(format_='QuantumCircuit'): 141 | self.log.info(circuit.qasm()) 142 | shots = 100 143 | job_pq = execute(circuit, global_pq_simulator, shots=shots, seed_simulator=1) 144 | job_qk = execute(circuit, qk_simulator, shots=shots, seed_simulator=1) 145 | result_pq = job_pq.result() 146 | result_qk = job_qk.result() 147 | counts_pq = result_pq.get_counts() 148 | counts_qk = result_qk.get_counts() 149 | self.log.info('local_qasm_simulator_jku: %s', str(counts_pq)) 150 | self.log.info('local_qasm_simulator: %s', str(counts_qk)) 151 | states = counts_qk.keys() | counts_pq.keys() 152 | # contingency table 153 | ctable = numpy.array([[counts_pq.get(key, 0) for key in states], 154 | [counts_qk.get(key, 0) for key in states]]) 155 | result = chi2_contingency(ctable) 156 | self.log.info('chi2_contingency: %s', str(result)) 157 | with self.subTest(circuit=circuit): 158 | self.assertGreater(result[1], 0.01) 159 | 160 | 161 | if __name__ == '__main__': 162 | unittest.main(verbosity=2) 163 | --------------------------------------------------------------------------------