├── .coveragerc ├── .github ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── format.yml │ ├── post_release_version_bump.yml │ ├── pre_release_version_bump.yml │ ├── tests.yml │ └── upload.yml ├── .gitignore ├── .pylintrc ├── .readthedocs.yml ├── LICENSE ├── Makefile ├── README.rst ├── doc ├── Makefile ├── _ext │ └── edit_on_github.py ├── _static │ ├── favicon.ico │ ├── logo.png │ ├── logo_new.png │ ├── logo_new_small.png │ ├── pennylane.png │ ├── pennylane.svg │ ├── puzzle_pq.png │ └── xanadu_x.png ├── code.rst ├── conf.py ├── devices │ ├── classical.rst │ ├── ibm.rst │ └── simulator.rst ├── index.rst ├── installation.rst ├── requirements.txt └── support.rst ├── pennylane_pq ├── __init__.py ├── _version.py ├── devices.py ├── expval.py ├── ops.py └── pqops.py ├── requirements.txt ├── setup.py └── tests ├── defaults.py ├── test_basis_state.py ├── test_compare_with_default_qubit.py ├── test_device_initialization.py ├── test_docs.py ├── test_ibm_expval_and_pre_expval_mock.py ├── test_projectq_import.py ├── test_unsupported_operations.py └── test_var.py /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | source = pennylane_pq 4 | 5 | [report] 6 | # Regexes for lines to exclude from consideration 7 | exclude_lines = 8 | # Have to re-enable the standard pragma 9 | pragma: no cover 10 | 11 | # Don't complain about missing debug-only code: 12 | def __repr__ 13 | def __eq__ 14 | if self\.debug 15 | 16 | # print statements 17 | def __str__ 18 | def __format__ 19 | def _print_list 20 | 21 | # Don't complain if non-runnable code isn't run: 22 | if 0: 23 | pass 24 | if __name__ == .__main__.: 25 | raise NotImplementedError 26 | 27 | ignore_errors = True 28 | 29 | [html] 30 | directory = coverage_html_report 31 | -------------------------------------------------------------------------------- /.github/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release 0.34.0 2 | 3 | ### Breaking changes 💔 4 | 5 | * Pin the PennyLane version to <0.35 6 | [(#98)](https://github.com/PennyLaneAI/pennylane-pq/pull/98) 7 | 8 | ### Contributors ✍️ 9 | 10 | This release contains contributions from (in alphabetical order): 11 | 12 | Matthew Silverman 13 | 14 | --- 15 | # Release 0.33.0 16 | 17 | ### Breaking changes 💔 18 | 19 | * Support for Python 3.8 has been removed, and support for Python 3.11 has been added. 20 | [(#88)](https://github.com/PennyLaneAI/pennylane-pq/pull/88) 21 | 22 | ### Contributors ✍️ 23 | 24 | This release contains contributions from (in alphabetical order): 25 | 26 | Mudit Pandey 27 | 28 | --- 29 | 30 | # Release 0.31.0 31 | 32 | ### Breaking changes 33 | 34 | * Remove python 3.7 support. 35 | [(#81)](https://github.com/PennyLaneAI/pennylane-pq/pull/81) 36 | 37 | ### Improvements 38 | 39 | * Update and simplify tests comparing with default.qubit to be compatible with 40 | a more recent version of PennyLane. 41 | [(#75)](https://github.com/PennyLaneAI/pennylane-pq/pull/75) 42 | 43 | * Use the S an T gates provided by PennyLane instead of custom gates defined by the plugin. 44 | [(#75)](https://github.com/PennyLaneAI/pennylane-pq/pull/75) 45 | 46 | ### Contributors 47 | 48 | This release contains contributions from (in alphabetical order): 49 | 50 | Romain Moyard 51 | 52 | --- 53 | 54 | # Release 0.17.0 55 | 56 | ### Breaking changes 57 | 58 | * Remove Python 3.5 / 3.6 and add the compatibility tag for Python 3.8 / 3.9. 59 | [(#72)](https://github.com/PennyLaneAI/pennylane-pq/pull/72) 60 | 61 | ### Bug fixes 62 | 63 | * Remove `SparseHamiltonian` from authorized observables in tests. 64 | [(#72)](https://github.com/PennyLaneAI/pennylane-pq/pull/72) 65 | 66 | ### Contributors 67 | 68 | This release contains contributions from (in alphabetical order): 69 | 70 | Romain Moyard 71 | 72 | --- 73 | 74 | # Release 0.15.0 75 | 76 | ### Breaking changes 77 | 78 | * For compatibility with PennyLane v0.15, the `analytic` keyword argument 79 | has been removed from all devices. Analytic expectation values can 80 | still be computed by setting `shots=None`. 81 | [(#69)](https://github.com/PennyLaneAI/pennylane-pq/pull/69) 82 | 83 | ### Contributors 84 | 85 | This release contains contributions from (in alphabetical order): 86 | 87 | Josh Izaac 88 | 89 | --- 90 | 91 | # Release 0.11.0 92 | 93 | ### Improvements 94 | 95 | * The PennyLane-PQ plugin now supports ProjectQ v0.5.1 96 | [(#62)](https://github.com/PennyLaneAI/pennylane-pq/pull/62) 97 | 98 | * Updates the device to support lists of custom wire labels. 99 | [(#65)](https://github.com/PennyLaneAI/pennylane-pq/pull/65) 100 | 101 | ### Documentation 102 | 103 | * The documentation theme has been updated, and the documentation structure 104 | reorganized. 105 | [(#60)](https://github.com/PennyLaneAI/pennylane-pq/pull/60) 106 | 107 | ### Bug fixes 108 | 109 | * Updated the plugin to use the latest IBMQBackend from ProjectQ. 110 | [(#62)](https://github.com/PennyLaneAI/pennylane-pq/pull/62) 111 | 112 | ### Contributors 113 | 114 | This release contains contributions from (in alphabetical order): 115 | 116 | Josh Izaac, Maria Schuld 117 | 118 | --- 119 | 120 | # Release 0.8.0 121 | 122 | ### Bug fixes 123 | 124 | * Adding the 'model': 'qubit' entry into the capabilities dictionary. Adjusting tests that previously used CV operators to use custom created operators. 125 | ([#56](https://github.com/PennyLaneAI/pennylane-pq/pull/56)) 126 | 127 | ### Contributors 128 | 129 | This release contains contributions from (in alphabetical order): 130 | 131 | Antal Szava 132 | 133 | --- 134 | 135 | # Version 0.6.0 136 | 137 | ### Bug fixes 138 | 139 | * The way measurement statistics works has changed in the latest version of PennyLane. Now, rather 140 | than `shots=0` referring to 'analytic' mode, there is a separate analytic argument. 141 | Further, the num_shots argument has been removed from Device.samples(). 142 | ([#53](https://github.com/PennyLaneAI/pennylane-pq/pull/53)) 143 | 144 | ### Contributors 145 | 146 | This release contains contributions from (in alphabetical order): 147 | 148 | Josh Izaac 149 | 150 | --- 151 | 152 | # Version 0.4.1 153 | 154 | ### Bug fixes 155 | 156 | * Remove opening of `requirements.txt` from within `setup.py`. This avoids a `FileNotFoundError` if installing via `pip`, as Python renames this file during packaging to `requires.txt`. 157 | ([#53](https://github.com/PennyLaneAI/pennylane-pq/pull/53)) 158 | 159 | ### Contributors 160 | 161 | This release contains contributions from (in alphabetical order): 162 | 163 | Josh Izaac 164 | 165 | --- 166 | 167 | # Version 0.4.0 168 | 169 | ### New features 170 | 171 | * Adds support for PennyLane version 0.4. This includes: 172 | 173 | - Renaming expectation -> observable in various places in the documentation 174 | - Device.expectations -> Device.observables 175 | - Device.pre_expval -> Device.pre_measure 176 | - Device.post_expval -> Device. 177 | 178 | * Adds support for `pennylane.var()` QNode return type 179 | 180 | * Updates the minimum required PennyLane version to 0.4 181 | 182 | * Update the documentation to reflect PennyLane v0.4 support 183 | 184 | ### Contributors 185 | 186 | This release contains contributions from (in alphabetical order): 187 | 188 | Josh Izaac 189 | 190 | --- 191 | 192 | # Version 0.2.1 193 | 194 | ### Bug fixes 195 | 196 | * Allows PennyLane-ProjectQ devices to be used with all newer version of PennyLane, not just version 0.2.0. 197 | 198 | ### Contributors 199 | 200 | This release contains contributions from (in alphabetical order): 201 | 202 | Josh Izaac 203 | 204 | --- 205 | 206 | # Version 0.2 207 | 208 | ### New features 209 | 210 | * Adds support for PennyLane v0.2 211 | 212 | * Increases the number of supported measurements on the IBM backend by automatically applying operations before the the final measurement (which is always in the `PauliZ` basis) (#38) 213 | 214 | * Adds support for the new PennyLane Identity expectation in all devices provided by this plugin (#36) 215 | 216 | * The simulator backend now supports the simulation of finite statistics expectation values if shots!=0, by sampling from the exact probability distribution (#35) 217 | 218 | ### Improvements 219 | 220 | * Improved the documentation of specifying credentials via the configuration file (#48) 221 | 222 | * Improves the handling of kwargs and the corresponding documentation (#39) 223 | 224 | * Corrected and improved documentation of the supported operations 225 | 226 | ### Contributors 227 | 228 | This release contains contributions from (in alphabetical order): 229 | 230 | Christian Gogolin 231 | 232 | --- 233 | 234 | # Version 0.1 235 | 236 | First public release of PennyLane ProjectQ plugin. 237 | 238 | ### Contributors 239 | 240 | This release contains contributions from (in alphabetical order): 241 | 242 | Christian Gogolin, Maria Schuld, Josh Izaac, Nathan Killoran, and Ville Bergholm. 243 | -------------------------------------------------------------------------------- /.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 research@xanadu.ai. 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 to PennyLane-PQ 2 | 3 | Thank you for taking the time to contribute to the PennyLane plugin ecosystem! 4 | :confetti_ball: :tada: :fireworks: :balloon: 5 | 6 | PennyLane and its plugins are a collaborative effort with the quantum computation and machine learning 7 | community - while we will continue working on adding new and exciting features to PennyLane, 8 | we invite you to join us and suggest improvements, research ideas, or even just to discuss how 9 | PennyLane fits into your workflow. 10 | 11 | ## How can I get involved in the community? 12 | 13 | If you want to contribute but don't know where to start, start by checking out the main 14 | [PennyLane documentation](https://pennylane.readthedocs.io). Have a go working through some of the tutorials, 15 | and having a look at the PennyLane API and code documentation to see how things work under the hood. 16 | 17 | Finally, check out the contributing guidelines in the main 18 | [PennyLane repository](https://github.com/PennyLaneAI/pennylane/blob/master/.github/CONTRIBUTING.md). 19 | 20 | If you are interested in contributing to the PennyLane-PQ plugin directly, continue reading below. 21 | 22 | ## How can I contribute to PennyLane-PQ? 23 | 24 | * **Report bugs** - even if you are not using the development branch of PennyLane-PQ, if you come 25 | across any bugs or issues, make a bug report. See the next section for more details on the bug 26 | reporting procedure. 27 | 28 | * **Suggest new features and enhancements** - use the GitHub issue tracker 29 | and let us know what will make PennyLane-PQ even better for your workflow. 30 | 31 | * **Contribute to our documentation, or to PennyLane-PQ directly** - if you would like to add 32 | to our documentation, or suggest improvements/changes, let us know - or even submit a pull request directly. All authors 33 | who have contributed to the PennyLane codebase will be listed alongside the latest release. 34 | 35 | Appetite whetted? Keep reading below for all the nitty-gritty on reporting bugs, contributing to the documentation, 36 | and submitting pull requests. 37 | 38 | ## Reporting bugs 39 | 40 | We use the [GitHub issue tracker](https://github.com/PennyLaneAI/pennylane-pq/issues) to keep track of all reported 41 | bugs and issues. If you find a bug, or have an issue with PennyLane-PQ, please submit a bug report! User 42 | reports help us make PennyLane better on all fronts. 43 | 44 | To submit a bug report, please work your way through the following checklist: 45 | 46 | * **Search the issue tracker to make sure the bug wasn't previously reported**. If it was, you can add a comment 47 | to expand on the issue if you would like. 48 | 49 | * **Fill out the issue template**. If you cannot find an existing issue addressing the problem, create a new 50 | issue by filling out the [issue template](.github/ISSUE_TEMPLATE.md). This template is added automatically to the comment 51 | box when you create a new issue. Please try and add as many details as possible! 52 | 53 | * Try and make your issue as **clear, concise, and descriptive** as possible. Include a clear and descriptive title, 54 | and include all code snippets/commands required to reproduce the problem. If you're not sure what caused the issue, 55 | describe what you were doing when the issue occurred. 56 | 57 | ## Suggesting features, document additions, and enhancements 58 | 59 | To suggest features and enhancements, please use the GitHub tracker. There is no template required for 60 | feature requests and enhancements, but here are a couple of suggestions for things to include. 61 | 62 | * **Use a clear and descriptive title** 63 | * **Provide a step-by-step description of the suggested feature**. 64 | 65 | - If the feature is related to any theoretical results in quantum machine learning or quantum computation, 66 | provide any relevant equations. Alternatively, provide references to papers/preprints, 67 | with the relevant sections/equations noted. 68 | - If the feature is workflow-related, or related to the use of PennyLane, 69 | explain why the enhancement would be useful, and where/how you would like to use it. 70 | 71 | * **For documentation additions**, point us towards any relevant equations/papers/preprints, 72 | with the relevant sections/equations noted. Short descriptions of its use/importance would also be useful. 73 | 74 | ## Pull requests 75 | 76 | If you would like to contribute directly to the PennyLane-PQ codebase, simply make a fork of the master branch, and 77 | then when ready, submit a [pull request](https://help.github.com/articles/about-pull-requests). We encourage everyone 78 | to have a go forking and modifying the PennyLane source code, however, we have a couple of guidelines on pull 79 | requests to ensure the main master branch of PennyLane conforms to existing standards and quality. 80 | 81 | ### General guidelines 82 | 83 | * **Do not make a pull request for minor typos/cosmetic code changes** - make an issue instead. 84 | * **For major features, consider making an independent app** that runs on top of PennyLane, rather than modifying 85 | PennyLane directly. 86 | 87 | ### Before submitting 88 | 89 | Before submitting a pull request, please make sure the following is done: 90 | 91 | * **All new features must include a unit test.** If you've fixed a bug or added code that should be tested, 92 | add a test to the test directory! 93 | * **All new functions and code must be clearly commented and documented.** Have a look through the source code at some of 94 | the existing function docstrings - the easiest approach is to simply copy an existing docstring and modify it as appropriate. 95 | If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. 96 | * **Ensure that the test suite passes**, by running `make test`. 97 | * **Make sure the modified code in the pull request conforms to the PEP8 coding standard.** The PennyLane-PQ source code 98 | conforms to [PEP8 standards](https://www.python.org/dev/peps/pep-0008/). We check all of our code against 99 | [Pylint](https://www.pylint.org/). To lint modified files, simply install `pip install pylint`, and then from the source code 100 | directory, run `pylint pennylane/path/to/file.py`. 101 | 102 | ### Submitting the pull request 103 | * When ready, submit your fork as a [pull request](https://help.github.com/articles/about-pull-requests) to the PennyLane 104 | repository, filling out the [pull request template](.github/PULL_REQUEST_TEMPLATE.md). This template is added automatically 105 | to the comment box when you create a new issue. 106 | 107 | * When describing the pull request, please include as much detail as possible regarding the changes made/new features 108 | added/performance improvements. If including any bug fixes, mention the issue numbers associated with the bugs. 109 | 110 | * Once you have submitted the pull request, three things will automatically occur: 111 | 112 | - The **test suite** will automatically run to ensure that the all tests continue to pass. 113 | - Once the test suite is finished, a **code coverage report** will be generated on 114 | [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane). This will calculate the percentage of PennyLane 115 | covered by the test suite, to ensure that all new code additions are adequately tested. 116 | - Finally, the **code quality** is calculated to ensure all new code additions adhere to our code quality standards. 117 | 118 | Based on these reports, we may ask you to make small changes to your branch before merging the pull request into the master branch. Alternatively, you can also 119 | [grant us permission to make changes to your pull request branch](https://help.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork/). 120 | 121 | :fireworks: Thank you for contributing to PennyLane! :fireworks: 122 | 123 | \- The PennyLane team 124 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Before posting an issue 2 | 3 | Search existing GitHub issues to make sure the issue does not already exist: 4 | https://github.com/PennyLaneAI/pennylane-pq/issues 5 | 6 | If posting a PennyLane-PQ issue, delete everything above the dashed line, and fill in the template. 7 | 8 | If making a feature request, delete the following template and describe, in detail, the feature and why it is needed. 9 | 10 | For general technical details: 11 | * Check out our documentation: https://pennylane-pq.readthedocs.io 12 | 13 | ----------------------------------------------------------------------------------------------------------------------- 14 | 15 | #### Issue description 16 | 17 | Description of the issue - include code snippets and screenshots here if relevant. 18 | 19 | * *Expected behavior:* (What you expect to happen) 20 | 21 | * *Actual behavior:* (What actually happens) 22 | 23 | * *Reproduces how often:* (What percentage of the time does it reproduce?) 24 | 25 | 26 | #### System information 27 | 28 | * **Operating system:** 29 | Include the operating system version if you can, e.g. Ubuntu Linux 16.04 30 | 31 | 32 | * **PennyLane-PQ version:** 33 | This can be found by running 34 | python -c "import pennylane_pq as pq; print(pq.__version__)" 35 | 36 | * **Python version:** 37 | This can be found by running: python --version 38 | 39 | 40 | * **NumPy and SciPy versions:** 41 | These can be found by running 42 | python -c "import numpy as np; import scipy as sp; print(np.__version__,sp.__version__)" 43 | 44 | * **Installation method:** 45 | 46 | Did you install PennyLane-PQ via pip, or directly from the GitHub repository source code? 47 | 48 | If installed via GitHub, what branch/commit did you use? You can get this information by opening a terminal in the 49 | PennyLane-PQ source code directory, and pasting the output of: 50 | git log -n 1 --pretty=format:"%H%n%an%n%ad%n%s" 51 | 52 | 53 | #### Source code and tracebacks 54 | 55 | Please include any additional code snippets and error tracebacks related to the issue here. 56 | 57 | 58 | #### Additional information 59 | 60 | Any additional information, configuration or data that might be necessary to reproduce the issue. 61 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### General guidelines 2 | 3 | * Do not make a pull request for minor typos/cosmetic code changes - make an issue instead. 4 | 5 | ### Before submitting 6 | 7 | Before submitting a pull request, please make sure the following is done: 8 | 9 | * All new features must include a unit test. 10 | If you've fixed a bug or added code that should be tested, add a test to the test directory! 11 | 12 | * All new functions and code must be clearly commented and documented. 13 | Have a look through the source code at some of the existing function docstrings - the easiest 14 | approach is to simply copy an existing docstring and modify it as appropriate. 15 | If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. 16 | 17 | * Ensure that the test suite passes, by running `make test`. 18 | 19 | * Make sure the modified code in the pull request conforms to the PEP8 coding standard. 20 | The PennyLane source code conforms to PEP8 standards (https://www.python.org/dev/peps/pep-0008/). 21 | We check all of our code against Pylint (https://www.pylint.org/). To lint modified files, simply 22 | `pip install pylint`, and then from the source code directory, run `pylint pennylane/path/to/file.py`. 23 | 24 | ### Pull request template 25 | 26 | When ready to submit, delete everything above the dashed line and fill in the pull request template. 27 | 28 | Please include as much detail as possible regarding the changes made/new features 29 | added/performance improvements. If including any bug fixes, mention the issue numbers associated with the bugs. 30 | 31 | ------------------------------------------------------------------------------------------------------------ 32 | 33 | **Description of the Change:** 34 | 35 | **Benefits:** 36 | 37 | **Possible Drawbacks:** 38 | 39 | **Related GitHub Issues:** 40 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Formatting check 2 | on: 3 | - pull_request 4 | 5 | jobs: 6 | black: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Black Code Formatter 11 | uses: lgeiger/black-action@v1.0.1 12 | with: 13 | args: "-l 100 pennylane_pq/ --check" 14 | -------------------------------------------------------------------------------- /.github/workflows/post_release_version_bump.yml: -------------------------------------------------------------------------------- 1 | name: Automated Release Version Bumps 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | post_release_version_bump: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v2 14 | 15 | - name: Run post-release version bump 16 | uses: PennyLaneAI/automation/version_bump_action@main 17 | with: 18 | version_path: "./pennylane_pq/_version.py" 19 | changelog_path: "./.github/CHANGELOG.md" 20 | release_status: "post_release" 21 | 22 | - name: Create Pull Request 23 | uses: peter-evans/create-pull-request@v3 24 | with: 25 | commit-message: post release version bump 26 | title: Version Bump 27 | body: updated changelog and _version.py 28 | branch: post-release-version-bump 29 | reviewers: mudit2812 30 | base: master 31 | -------------------------------------------------------------------------------- /.github/workflows/pre_release_version_bump.yml: -------------------------------------------------------------------------------- 1 | name: Manually Triggered Version Bumps 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | pre_release_version_bump: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Clone repository 12 | uses: actions/checkout@v2 13 | 14 | - name: Run pre-release version bump 15 | uses: PennyLaneAI/automation/version_bump_action@main 16 | with: 17 | version_path: "./pennylane_pq/_version.py" 18 | changelog_path: "./.github/CHANGELOG.md" 19 | release_status: "pre_release" 20 | 21 | - name: Create Pull Request 22 | uses: peter-evans/create-pull-request@v3 23 | with: 24 | commit-message: pre release version bump 25 | title: Version Bump 26 | body: updated changelog and _version.py 27 | branch: pre-release-version-bump 28 | reviewers: mudit2812 29 | base: master 30 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: [3.9, '3.10', '3.11'] 14 | steps: 15 | - name: Cancel Previous Runs 16 | uses: styfle/cancel-workflow-action@0.4.1 17 | with: 18 | access_token: ${{ github.token }} 19 | - uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 2 22 | - name: Set up Python 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install -r requirements.txt 30 | pip install wheel pytest pytest-cov pytest-mock --upgrade 31 | - name: Install Plugin 32 | run: | 33 | python setup.py bdist_wheel 34 | pip install dist/PennyLane*.whl 35 | - name: Run tests 36 | env: 37 | IBMQX_TOKEN: ${{ secrets.IBMQX_TOKEN }} 38 | run: python -m pytest tests --cov=pennylane_pq --cov-report=term-missing --cov-report=xml -p no:warnings --tb=native 39 | - name: Upload coverage to Codecov 40 | uses: codecov/codecov-action@v1.0.12 41 | with: 42 | file: ./coverage.xml 43 | -------------------------------------------------------------------------------- /.github/workflows/upload.yml: -------------------------------------------------------------------------------- 1 | name: Upload 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | upload: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Set up Python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: 3.9 16 | 17 | - name: Build and install Plugin 18 | run: | 19 | python -m pip install --upgrade pip wheel 20 | python setup.py bdist_wheel 21 | pip install dist/PennyLane*.whl 22 | 23 | - name: Install test dependencies 24 | run: | 25 | pip install wheel pytest pytest-cov pytest-mock --upgrade 26 | 27 | - name: Run tests 28 | env: 29 | IBMQX_TOKEN: ${{ secrets.IBMQX_TOKEN }} 30 | run: | 31 | python -m pytest tests --tb=native 32 | 33 | - name: Publish 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | with: 36 | user: __token__ 37 | password: ${{ secrets.PYPI }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc/_build/ 2 | pennylane_pq.egg-info/ 3 | build/ 4 | dist/ 5 | pennylane_pq/__pycache__/ 6 | tests/__pycache__/ 7 | .idea 8 | *.ipynp 9 | *.ipynb_checkpoints 10 | __pycache__ 11 | PennyLane_PQ.egg-info/ 12 | .pytest_cache/ 13 | coverage_html_report/ 14 | .coverage 15 | doc/code/api/* 16 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | # A comma-separated list of package or module names from where C extensions may 3 | # be loaded. Extensions are loading into the active Python interpreter and may 4 | # run arbitrary code 5 | extension-pkg-whitelist=numpy,scipy,autograd,toml,appdir,pennylane,projectq 6 | 7 | [TYPECHECK] 8 | 9 | # List of module names for which member attributes should not be checked 10 | # (useful for modules/projects where namespaces are manipulated during runtime 11 | # and thus existing member attributes cannot be deduced by static analysis. It 12 | # supports qualified module names, as well as Unix pattern matching. 13 | ignored-modules=numpy,scipy,autograd,toml,appdir,pennylane,projectq 14 | 15 | # List of classes names for which member attributes should not be checked 16 | # (useful for classes with attributes dynamically set). This supports can work 17 | # with qualified names. 18 | ignored-classes=numpy,scipy,autograd,toml,appdir,pennylane,projectq 19 | 20 | [MESSAGES CONTROL] 21 | 22 | # Enable the message, report, category or checker with the given id(s). You can 23 | # either give multiple identifier separated by comma (,) or put this option 24 | # multiple time. 25 | #enable= 26 | 27 | # Disable the message, report, category or checker with the given id(s). You 28 | # can either give multiple identifier separated by comma (,) or put this option 29 | # multiple time (only on the command line, not in the configuration file where 30 | # it should appear only once). 31 | disable=line-too-long,invalid-name,too-many-lines,redefined-builtin,too-many-locals,duplicate-code -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: doc/conf.py 11 | 12 | # Build documentation with MkDocs 13 | #mkdocs: 14 | # configuration: mkdocs.yml 15 | 16 | # Optionally build your docs in additional formats such as PDF and ePub 17 | formats: 18 | - htmlzip 19 | 20 | # Optionally set the version of Python and requirements required to build your docs 21 | python: 22 | install: 23 | - requirements: doc/requirements.txt 24 | - method: pip 25 | path: . 26 | 27 | build: 28 | os: ubuntu-22.04 29 | tools: 30 | python: "3.9" 31 | apt_packages: 32 | - graphviz 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON3 := $(shell which python3 2>/dev/null) 2 | 3 | PYTHON := python3 4 | COVERAGE := --cov=pennylane_pq --cov-report term-missing --cov-report=html:coverage_html_report 5 | TESTRUNNER := -m pytest tests --tb=short 6 | 7 | .PHONY: help 8 | help: 9 | @echo "Please use \`make ' where is one of" 10 | @echo " install to install PennyLane-PQ" 11 | @echo " wheel to build the PennyLane-PQ wheel" 12 | @echo " dist to package the source distribution" 13 | @echo " clean to delete all temporary, cache, and build files" 14 | @echo " clean-docs to delete all built documentation" 15 | @echo " test to run the test suite for all configured devices" 16 | @echo " test-[device] to run the test suite for the device simulator or ibm" 17 | @echo " coverage to generate a coverage report for all configured devices" 18 | @echo " coverage-[device] to generate a coverage report for the device simulator or ibm" 19 | 20 | .PHONY: install 21 | install: 22 | ifndef PYTHON3 23 | @echo "To install PennyLane-PQ you need to have Python 3 installed" 24 | endif 25 | $(PYTHON) setup.py install 26 | 27 | .PHONY: wheel 28 | wheel: 29 | $(PYTHON) setup.py bdist_wheel 30 | 31 | .PHONY: dist 32 | dist: 33 | $(PYTHON) setup.py sdist 34 | 35 | .PHONY : clean 36 | clean: 37 | rm -rf pennylane_pq/__pycache__ 38 | rm -rf tests/__pycache__ 39 | rm -rf dist 40 | rm -rf build 41 | rm -rf .pytest_cache 42 | rm -rf .coverage coverage_html_report/ 43 | 44 | docs: 45 | make -C doc html 46 | 47 | .PHONY : clean-docs 48 | clean-docs: 49 | make -C doc clean 50 | 51 | 52 | test: test-all 53 | 54 | test-%: 55 | @echo "Testing device: $(subst test-,,$@)..." 56 | export DEVICE=$(subst test-,,$@) && $(PYTHON) $(TESTRUNNER) 57 | 58 | coverage: coverage-all 59 | 60 | coverage-%: 61 | @echo "Generating coverage report..." 62 | export DEVICE=$(subst coverage-,,$@) && $(PYTHON) $(TESTRUNNER) $(COVERAGE) 63 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | PennyLane ProjectQ Plugin 2 | ######################### 3 | 4 | .. image:: https://img.shields.io/github/actions/workflow/status/PennyLaneAI/pennylane-pq/tests.yml?branch=master&logo=github&style=flat-square 5 | :alt: GitHub Workflow Status (branch) 6 | :target: https://github.com/PennyLaneAI/pennylane-pq/actions?query=workflow%3ATests 7 | 8 | .. image:: https://img.shields.io/codecov/c/github/PennyLaneAI/pennylane-pq/master.svg?logo=codecov&style=flat-square 9 | :alt: Codecov coverage 10 | :target: https://codecov.io/gh/PennyLaneAI/pennylane-pq 11 | 12 | .. image:: https://img.shields.io/codefactor/grade/github/PennyLaneAI/pennylane-pq/master?logo=codefactor&style=flat-square 13 | :alt: CodeFactor Grade 14 | :target: https://www.codefactor.io/repository/github/pennylaneai/pennylane-pq 15 | 16 | .. image:: https://readthedocs.com/projects/xanaduai-pennylane-pq/badge/?version=latest&style=flat-square 17 | :alt: Read the Docs 18 | :target: https://docs.pennylane.ai/projects/projectq 19 | 20 | .. image:: https://img.shields.io/pypi/v/PennyLane-pq.svg?style=flat-square 21 | :alt: PyPI 22 | :target: https://pypi.org/project/PennyLane-pq 23 | 24 | .. image:: https://img.shields.io/pypi/pyversions/PennyLane-pq.svg?style=flat-square 25 | :alt: PyPI - Python Version 26 | :target: https://pypi.org/project/PennyLane-pq 27 | 28 | \ 29 | 30 | **❗ This plugin will not be supported in newer versions of PennyLane. It is compatible with versions 31 | of PennyLane up to and including 0.34❗** 32 | 33 | .. header-start-inclusion-marker-do-not-remove 34 | 35 | The PennyLane-ProjectQ plugin integrates the ProjectQ quantum computing library with PennyLane's 36 | quantum machine learning capabilities. 37 | 38 | `PennyLane `_ is a cross-platform Python library for quantum machine 39 | learning, automatic differentiation, and optimization of hybrid quantum-classical computations. 40 | 41 | `ProjectQ `_ is an open-source compilation framework capable of 42 | targeting various types of hardware and a high-performance quantum computer simulator with 43 | emulation capabilities, and various compiler plug-ins. 44 | 45 | This PennyLane plugin allows to use both the software and hardware backends of ProjectQ as devices for PennyLane. 46 | 47 | .. header-end-inclusion-marker-do-not-remove 48 | 49 | The documentation can be found at https://pennylane-pq.readthedocs.io. 50 | 51 | 52 | Features 53 | ======== 54 | 55 | * Provides three devices to be used with PennyLane: ``projectq.simulator``, ``projectq.ibm``, and ``projectq.classical``. These provide access to the respective ProjectQ backends. 56 | 57 | * Supports a wide range of PennyLane operations and observables across the devices. 58 | 59 | * Combine ProjectQ high performance simulator and hardware backend support with PennyLane's automatic differentiation and optimization. 60 | 61 | .. installation-start-inclusion-marker-do-not-remove 62 | 63 | Installation 64 | ============ 65 | 66 | This plugin requires Python version 3.5 and above, as well as PennyLane and ProjectQ. Installation of this plugin, as well as all dependencies, can be done using pip: 67 | 68 | .. code-block:: bash 69 | 70 | $ python -m pip install pennylane_pq 71 | 72 | To test that the PennyLane ProjectQ plugin is working correctly you can run 73 | 74 | .. code-block:: bash 75 | 76 | $ make test 77 | 78 | in the source folder. Tests restricted to a specific device can be run by executing 79 | :code:`make test-simulator`, :code:`make test-ibm`, or :code:`make test-classical`. 80 | 81 | .. note:: 82 | 83 | Tests on the `ibm device `_ 84 | can only be run if a :code:`user` and :code:`password` for the 85 | `IBM Q experience `_ are configured 86 | in the `PennyLane configuration file `_. 87 | If this is the case, running :code:`make test` also executes tests on the :code:`ibm` device. 88 | By default tests on the :code:`ibm` device run with :code:`hardware=False`. At the time of writing this 89 | means that the test are "free". Please verify that this is also the case for your account. 90 | 91 | .. installation-end-inclusion-marker-do-not-remove 92 | 93 | Please refer to the `documentation of the PennyLane ProjectQ Plugin `_ 94 | as well as well as to the `documentation of PennyLane `_ for further reference. 95 | 96 | .. howtocite-start-inclusion-marker-do-not-remove 97 | 98 | How to cite 99 | =========== 100 | 101 | If you are doing research using PennyLane, please cite `our whitepaper `_: 102 | 103 | Ville Bergholm, Josh Izaac, Maria Schuld, Christian Gogolin, and Nathan Killoran. PennyLane. *arXiv*, 2018. arXiv:1811.04968 104 | 105 | .. howtocite-end-inclusion-marker-do-not-remove 106 | 107 | Contributing 108 | ============ 109 | 110 | We welcome contributions - simply fork the repository of this plugin, and then make a 111 | `pull request `_ containing your contribution. 112 | All contributers to this plugin will be listed as authors on the releases. 113 | 114 | We also encourage bug reports, suggestions for new features and enhancements, and even 115 | links to cool projects or applications built on PennyLane. 116 | 117 | 118 | Authors 119 | ======= 120 | 121 | Christian Gogolin, Maria Schuld, Josh Izaac, Nathan Killoran, and Ville Bergholm 122 | 123 | .. support-start-inclusion-marker-do-not-remove 124 | 125 | Support 126 | ======= 127 | 128 | - **Source Code:** https://github.com/PennyLaneAI/pennylane-pq 129 | - **Issue Tracker:** https://github.com/PennyLaneAI/pennylane-pq/issues 130 | - **PennyLane Forum:** https://discuss.pennylane.ai 131 | 132 | If you are having issues, please let us know by posting the issue on our Github issue tracker, or 133 | by asking a question in the forum. 134 | 135 | .. support-end-inclusion-marker-do-not-remove 136 | .. license-start-inclusion-marker-do-not-remove 137 | 138 | License 139 | ======= 140 | 141 | The PennyLane ProjectQ plugin is **free** and **open source**, released under 142 | the `Apache License, Version 2.0 `_. 143 | 144 | .. license-end-inclusion-marker-do-not-remove 145 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python3 -msphinx 7 | SPHINXPROJ = PennyLane 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/_ext/edit_on_github.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sphinx extension to add ReadTheDocs-style "Edit on GitHub" links to the 3 | sidebar. 4 | Loosely based on https://github.com/astropy/astropy/pull/347 5 | """ 6 | 7 | import os 8 | import warnings 9 | 10 | 11 | __licence__ = 'BSD (3 clause)' 12 | 13 | 14 | def get_github_url(app, view, path): 15 | return 'https://github.com/{project}/{view}/{branch}/{path}'.format( 16 | project=app.config.edit_on_github_project, 17 | view=view, 18 | branch=app.config.edit_on_github_branch, 19 | path=path) 20 | 21 | 22 | def html_page_context(app, pagename, templatename, context, doctree): 23 | if templatename != 'page.html': 24 | return 25 | 26 | if not app.config.edit_on_github_project: 27 | warnings.warn("edit_on_github_project not specified") 28 | return 29 | 30 | if not doctree: 31 | return 32 | 33 | path = os.path.relpath(doctree.get('source'), app.builder.srcdir) 34 | show_url = get_github_url(app, 'blob', path) 35 | edit_url = get_github_url(app, 'edit', path) 36 | 37 | context['show_on_github_url'] = show_url 38 | context['edit_on_github_url'] = edit_url 39 | 40 | 41 | def setup(app): 42 | app.add_config_value('edit_on_github_project', '', True) 43 | app.add_config_value('edit_on_github_branch', 'master', True) 44 | app.connect('html-page-context', html_page_context) -------------------------------------------------------------------------------- /doc/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/favicon.ico -------------------------------------------------------------------------------- /doc/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/logo.png -------------------------------------------------------------------------------- /doc/_static/logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/logo_new.png -------------------------------------------------------------------------------- /doc/_static/logo_new_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/logo_new_small.png -------------------------------------------------------------------------------- /doc/_static/pennylane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/pennylane.png -------------------------------------------------------------------------------- /doc/_static/pennylane.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 33 | 35 | 37 | 38 | 41 | 44 | 47 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 69 | 72 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /doc/_static/puzzle_pq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/puzzle_pq.png -------------------------------------------------------------------------------- /doc/_static/xanadu_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PennyLaneAI/pennylane-pq/854f103f0c21fcff6ffa83b0b1db365c4f5714a9/doc/_static/xanadu_x.png -------------------------------------------------------------------------------- /doc/code.rst: -------------------------------------------------------------------------------- 1 | pennylane-pq 2 | ============ 3 | 4 | This section contains the API documentation for the PennyLane-ProjectQ plugin. 5 | 6 | .. warning:: 7 | 8 | Unless you are a PennyLane plugin developer, you likely do not need 9 | to use these classes and functions directly. 10 | 11 | See the :doc:`overview ` page for more 12 | details using the available ProjectQ devices with PennyLane. 13 | 14 | .. currentmodule:: pennylane_pq 15 | 16 | .. automodapi:: pennylane_pq 17 | :no-heading: 18 | :include-all-objects: 19 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # PennyLane-PQ documentation build configuration file. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys, os, re 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | sys.path.insert(0, os.path.abspath('..')) 21 | sys.path.insert(0, os.path.abspath('_ext')) 22 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath('.')), 'doc')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | needs_sphinx = '1.6' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | "sphinx.ext.autodoc", 34 | "sphinx.ext.autosummary", 35 | "sphinx.ext.todo", 36 | "sphinx.ext.coverage", 37 | "sphinx.ext.mathjax", 38 | "sphinx.ext.napoleon", 39 | "sphinx.ext.inheritance_diagram", 40 | "sphinx.ext.intersphinx", 41 | 'sphinx.ext.viewcode', 42 | "sphinx_automodapi.automodapi" 43 | ] 44 | 45 | autosummary_generate = True 46 | autosummary_imported_members = False 47 | automodapi_toctreedirnm = "code/api" 48 | automodsumm_inherited_members = True 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | from pennylane_sphinx_theme import templates_dir 52 | templates_path = [templates_dir()] 53 | 54 | # The suffix(es) of source filenames. 55 | # You can specify multiple suffix as a list of string: 56 | # 57 | # source_suffix = ['.rst', '.md'] 58 | source_suffix = '.rst' 59 | 60 | # The master toctree document. 61 | master_doc = 'index' 62 | 63 | # General information about the project. 64 | project = 'PennyLane-ProjectQ' 65 | copyright = "2023, Xanadu Inc." 66 | author = 'Xanadu' 67 | 68 | add_module_names = False 69 | 70 | # The version info for the project you're documenting, acts as replacement for 71 | # |version| and |release|, also used in various other places throughout the 72 | # built documents. 73 | 74 | import pennylane_pq 75 | # The full version, including alpha/beta/rc tags. 76 | release = pennylane_pq.__version__ 77 | 78 | # The short X.Y version. 79 | version = re.match(r'^(\d+\.\d+)', release).expand(r'\1') 80 | 81 | # The language for content autogenerated by Sphinx. Refer to documentation 82 | # for a list of supported languages. 83 | # 84 | # This is also used if you do content translation via gettext catalogs. 85 | # Usually you set "language" from the command line for these cases. 86 | language = None 87 | 88 | # today_fmt is used as the format for a strftime call. 89 | today_fmt = '%Y-%m-%d' 90 | 91 | # List of patterns, relative to source directory, that match files and 92 | # directories to ignore when looking for source files. 93 | # This patterns also effect to html_static_path and html_extra_path 94 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | show_authors = True 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = 'sphinx' 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = True 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | # html_theme = 'nature' 113 | 114 | # Theme options are theme-specific and customize the look and feel of a theme 115 | # further. For a list of options available for each theme, see the 116 | # documentation. 117 | #html_theme_options = {} 118 | 119 | # Add any paths that contain custom themes here, relative to this directory. 120 | #html_theme_path = [] 121 | 122 | # The name for this set of Sphinx documents. If None, it defaults to 123 | # " v documentation". 124 | #html_title = None 125 | 126 | # A shorter title for the navigation bar. Default is the same as html_title. 127 | #html_short_title = None 128 | 129 | # The name of an image file (relative to this directory) to place at the top 130 | # of the sidebar. 131 | #html_logo = None 132 | 133 | # The name of an image file (relative to this directory) to use as a favicon of 134 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 135 | # pixels large. 136 | html_favicon = '_static/favicon.ico' 137 | 138 | # Add any paths that contain custom static files (such as style sheets) here, 139 | # relative to this directory. They are copied after the builtin static files, 140 | # so a file named "default.css" will overwrite the builtin "default.css". 141 | html_static_path = ['_static'] 142 | 143 | # Add any extra paths that contain custom files (such as robots.txt or 144 | # .htaccess) here, relative to this directory. These files are copied 145 | # directly to the root of the documentation. 146 | #html_extra_path = [] 147 | 148 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 149 | # using the given strftime format. 150 | #html_last_updated_fmt = '%b %d, %Y' 151 | 152 | # If true, SmartyPants will be used to convert quotes and dashes to 153 | # typographically correct entities. 154 | #html_use_smartypants = True 155 | 156 | # Custom sidebar templates, must be a dictionary that maps document names 157 | # to template names. 158 | # 159 | # This is required for the alabaster theme 160 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 161 | #html_sidebars = { 162 | # '**': [ 163 | # 'about.html', 164 | # 'navigation.html', 165 | # 'relations.html', # needs 'show_related': True theme option to display 166 | # 'searchbox.html', 167 | # 'donate.html', 168 | # ] 169 | #} 170 | 171 | # Additional templates that should be rendered to pages, maps page names to 172 | # template names. 173 | #html_additional_pages = {} 174 | 175 | # If false, no module index is generated. 176 | #html_domain_indices = True 177 | 178 | # If false, no index is generated. 179 | #html_use_index = True 180 | 181 | # If true, the index is split into individual pages for each letter. 182 | #html_split_index = False 183 | 184 | # If true, links to the reST sources are added to the pages. 185 | #html_show_sourcelink = True 186 | 187 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 188 | #html_show_sphinx = True 189 | 190 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 191 | #html_show_copyright = True 192 | 193 | # If true, an OpenSearch description file will be output, and all pages will 194 | # contain a tag referring to it. The value of this option must be the 195 | # base URL from which the finished HTML is served. 196 | #html_use_opensearch = '' 197 | 198 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 199 | #html_file_suffix = None 200 | 201 | # Language to be used for generating the HTML full-text search index. 202 | # Sphinx supports the following languages: 203 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 204 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 205 | #html_search_language = 'en' 206 | 207 | # A dictionary with options for the search language support, empty by default. 208 | # Now only 'ja' uses this config value 209 | #html_search_options = {'type': 'default'} 210 | 211 | # The name of a javascript file (relative to the configuration directory) that 212 | # implements a search results scorer. If empty, the default will be used. 213 | #html_search_scorer = 'scorer.js' 214 | 215 | # Output file base name for HTML help builder. 216 | htmlhelp_basename = 'PennyLanePQdoc' 217 | 218 | # -- Xanadu theme --------------------------------------------------------- 219 | html_theme = 'pennylane' 220 | 221 | # Register the theme as an extension to generate a sitemap.xml 222 | # extensions.append("guzzle_sphinx_theme") 223 | 224 | # xanadu theme options (see theme.conf for more information) 225 | html_theme_options = { 226 | "navbar_name": "PennyLane-ProjectQ", 227 | "extra_copyrights": [ 228 | "TensorFlow, the TensorFlow logo, and any related marks are trademarks " 229 | "of Google Inc." 230 | ], 231 | "toc_overview": True, 232 | "navbar_active_link": 3, 233 | "google_analytics_tracking_id": "G-C480Z9JL0D" 234 | } 235 | 236 | edit_on_github_project = "PennyLaneAI/pennylane-pq" 237 | edit_on_github_branch = "master/doc" 238 | 239 | # -- Options for HTMLHelp output ------------------------------------------ 240 | 241 | # Output file base name for HTML help builder. 242 | htmlhelp_basename = 'PennyLanePQdoc' 243 | 244 | 245 | # -- Options for LaTeX output --------------------------------------------- 246 | 247 | latex_elements = { 248 | # The paper size ('letterpaper' or 'a4paper'). 249 | # 250 | # 'papersize': 'letterpaper', 251 | 252 | # The font size ('10pt', '11pt' or '12pt'). 253 | # 254 | # 'pointsize': '10pt', 255 | 256 | # Additional stuff for the LaTeX preamble. 257 | # 258 | # 'preamble': '', 259 | 260 | # Latex figure (float) alignment 261 | # 262 | # 'figure_align': 'htbp', 263 | } 264 | 265 | latex_additional_files = ['macros.tex'] 266 | 267 | # Grouping the document tree into LaTeX files. List of tuples 268 | # (source start file, target name, title, 269 | # author, documentclass [howto, manual, or own class]). 270 | latex_documents = [ 271 | (master_doc, 'PennyLane-PQ.tex', 272 | 'PennyLane-ProjectQ Documentation', 273 | 'Xanadu Inc.', 274 | 'manual'), 275 | ] 276 | 277 | 278 | # -- Options for manual page output --------------------------------------- 279 | 280 | # One entry per manual page. List of tuples 281 | # (source start file, name, description, authors, manual section). 282 | man_pages = [ 283 | (master_doc, 'pennylane-pq', 284 | 'PennyLane-ProjectQ Documentation', 285 | [author], 1) 286 | ] 287 | 288 | 289 | # -- Options for Texinfo output ------------------------------------------- 290 | 291 | # Grouping the document tree into Texinfo files. List of tuples 292 | # (source start file, target name, title, author, 293 | # dir menu entry, description, category) 294 | texinfo_documents = [ 295 | (master_doc, 296 | 'PennyLane-ProjectQ', 297 | 'PennyLane-ProjectQ Documentation', 298 | author, 299 | 'PennyLane-ProjectQ', 300 | 'ProjectQ plugin for the PennyLane quantum machine learning library.', 301 | 'Miscellaneous'), 302 | ] 303 | 304 | 305 | #============================================================ 306 | 307 | # the order in which autodoc lists the documented members 308 | autodoc_member_order = 'bysource' 309 | 310 | # inheritance_diagram graphviz attributes 311 | inheritance_node_attrs = dict(color='lightskyblue1', style='filled') 312 | 313 | #autodoc_default_flags = ['members'] 314 | autosummary_generate = True 315 | -------------------------------------------------------------------------------- /doc/devices/classical.rst: -------------------------------------------------------------------------------- 1 | The Classical device 2 | ==================== 3 | 4 | This device is based on ProjectQ's 5 | `"classical" simulator backend `_, 6 | which only allows classical gates such as NOTs. 7 | 8 | The device is created in PennyLane as follows: 9 | 10 | .. code-block:: python 11 | 12 | import pennylane as qml 13 | dev = qml.device('projectq.classical', wires=XXX) 14 | -------------------------------------------------------------------------------- /doc/devices/ibm.rst: -------------------------------------------------------------------------------- 1 | The IBM device 2 | ============== 3 | 4 | This device uses ProjectQ's interfaces with the 5 | `IBM Quantum Experience backend `_, 6 | which includes a simulator and access to the hardware. 7 | 8 | For this you need to instantiate a :code:`'projectq.ibm'` 9 | device by giving your IBM Quantum Experience username and password: 10 | 11 | .. code-block:: python 12 | 13 | import pennylane as qml 14 | dev = qml.device('projectq.ibm', wires=2, user="XXX", password="XXX") 15 | 16 | Storing your credentials 17 | ~~~~~~~~~~~~~~~~~~~~~~~~ 18 | 19 | In order to avoid accidentally publishing your credentials, you should specify them 20 | via the `PennyLane configuration file `_ 21 | by adding a section such as 22 | 23 | .. code:: 24 | 25 | [projectq.global] 26 | 27 | [projectq.ibm] 28 | user = "XXX" 29 | password = "XXX" 30 | -------------------------------------------------------------------------------- /doc/devices/simulator.rst: -------------------------------------------------------------------------------- 1 | The Simulator device 2 | ==================== 3 | 4 | You can instantiate a :code:`projectq.simulator` device for PennyLane with: 5 | 6 | .. code-block:: python 7 | 8 | import pennylane as qml 9 | 10 | dev = qml.device('projectq.simulator', wires=2) 11 | 12 | This device can then be used just like other devices for 13 | the definition and evaluation of QNodes within PennyLane. 14 | 15 | A simple quantum function that returns 16 | the expectation value of a measurement and depends on three classical input parameters would look like: 17 | 18 | .. code-block:: python 19 | 20 | @qml.qnode(dev) 21 | def circuit(x, y, z): 22 | qml.RZ(z, wires=[0]) 23 | qml.RY(y, wires=[0]) 24 | qml.RX(x, wires=[0]) 25 | qml.CNOT(wires=[0, 1]) 26 | return qml.expval(qml.PauliZ(wires=1)) 27 | 28 | You can then execute the circuit like any other function to get the quantum mechanical expectation value. 29 | 30 | .. code-block:: python 31 | 32 | circuit(0.2, 0.1, 0.3) 33 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | PennyLane-ProjectQ Plugin 2 | ######################### 3 | 4 | :Release: |release| 5 | 6 | .. warning:: 7 | 8 | This plugin will not be supported in newer versions of PennyLane. It is compatible with versions 9 | of PennyLane up to and including 0.34. 10 | 11 | .. image:: _static/puzzle_pq.png 12 | :align: center 13 | :width: 70% 14 | :target: javascript:void(0); 15 | 16 | | 17 | 18 | .. include:: ../README.rst 19 | :start-after: header-start-inclusion-marker-do-not-remove 20 | :end-before: header-end-inclusion-marker-do-not-remove 21 | 22 | Once Pennylane-ProjectQ is installed, the provided ProjectQ devices can be accessed straight 23 | away in PennyLane, without the need to import any additional packages. 24 | 25 | Devices 26 | ~~~~~~~ 27 | 28 | PennyLane-ProjectQ provides three ProjectQ devices for PennyLane: 29 | 30 | .. title-card:: 31 | :name: 'projectq.simulator' 32 | :description: ProjectQ's basic simulator backend. 33 | :link: devices/simulator.html 34 | 35 | .. title-card:: 36 | :name: 'projectq.ibm' 37 | :description: ProjectQ's integration with the IBM Quantum Experience. 38 | :link: devices/ibm.html 39 | 40 | .. title-card:: 41 | :name: 'projectq.classical' 42 | :description: Simple simulator for classical computations. 43 | :link: devices/classical.html 44 | 45 | .. raw:: html 46 | 47 |
48 |
49 | 50 | Tutorials 51 | ~~~~~~~~~ 52 | 53 | To see the PennyLane-ProjectQ plugin in action, you can use any of the qubit based `demos 54 | from the PennyLane documentation `_, for example 55 | the tutorial on `qubit rotation `_, 56 | and simply replace ``'default.qubit'`` with any of the available ProjectQ devices, such as ``'projectq.simulator'``: 57 | 58 | .. code-block:: python 59 | 60 | dev = qml.device('projectq.simulator', wires=XXX) 61 | 62 | You can also try to run tutorials, such as the qubit rotation tutorial, on actual quantum hardware by 63 | using the ``'projectq.ibm'`` device. 64 | 65 | 66 | .. toctree:: 67 | :maxdepth: 2 68 | :titlesonly: 69 | :hidden: 70 | 71 | installation 72 | support 73 | 74 | .. toctree:: 75 | :maxdepth: 2 76 | :caption: Usage 77 | :hidden: 78 | 79 | devices/simulator 80 | devices/ibm 81 | devices/classical 82 | 83 | .. toctree:: 84 | :maxdepth: 1 85 | :caption: API 86 | :hidden: 87 | 88 | code 89 | -------------------------------------------------------------------------------- /doc/installation.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | :start-after: installation-start-inclusion-marker-do-not-remove 3 | :end-before: installation-end-inclusion-marker-do-not-remove 4 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | docutils==0.16 2 | sphinxcontrib-bibtex 3 | ipykernel 4 | nbsphinx 5 | pygments-github-lexers 6 | pybind11 7 | setuptools-scm 8 | sphinx-automodapi 9 | pennylane-sphinx-theme 10 | -------------------------------------------------------------------------------- /doc/support.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | :start-after: support-start-inclusion-marker-do-not-remove 3 | :end-before: support-end-inclusion-marker-do-not-remove 4 | -------------------------------------------------------------------------------- /pennylane_pq/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ """ 15 | from ._version import __version__ 16 | 17 | from .devices import ProjectQSimulator # pylint: disable=unused-import 18 | from .devices import ProjectQIBMBackend # pylint: disable=unused-import 19 | from .devices import ProjectQClassicalSimulator # pylint: disable=unused-import 20 | 21 | from .ops import * # pylint: disable=wildcard-import,unused-wildcard-import 22 | -------------------------------------------------------------------------------- /pennylane_pq/_version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Version information. 16 | Version number (major.minor.patch[-label]) 17 | """ 18 | 19 | __version__ = "0.34.0" 20 | -------------------------------------------------------------------------------- /pennylane_pq/devices.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # pylint: disable=expression-not-assigned 15 | r""" 16 | Devices 17 | ======= 18 | 19 | .. currentmodule:: pennylane_pq.devices 20 | 21 | This plugin offers access to the following ProjectQ backends by providing 22 | corresponding PennyLane devices: 23 | 24 | .. autosummary:: 25 | :nosignatures: 26 | 27 | ProjectQSimulator 28 | ProjectQIBMBackend 29 | ProjectQClassicalSimulator 30 | 31 | See below for a description of the devices and the supported Operations and Observables. 32 | 33 | ProjectQSimulator 34 | ################# 35 | 36 | .. autoclass:: ProjectQSimulator 37 | 38 | ProjectQIBMBackend 39 | ################## 40 | 41 | .. autoclass:: ProjectQIBMBackend 42 | 43 | ProjectQClassicalSimulator 44 | ########################## 45 | 46 | .. autoclass:: ProjectQClassicalSimulator 47 | 48 | 49 | """ 50 | import numpy as np 51 | import projectq as pq 52 | from projectq.setups.ibm import get_engine_list 53 | from projectq.ops import ( 54 | HGate, 55 | XGate, 56 | YGate, 57 | ZGate, 58 | SGate, 59 | TGate, 60 | SqrtXGate, 61 | SwapGate, 62 | Rx, 63 | Ry, 64 | Rz, 65 | R, 66 | SqrtSwapGate, 67 | ) 68 | 69 | from pennylane import Device, DeviceError 70 | 71 | from .pqops import CNOT, CZ, Rot, QubitUnitary, BasisState 72 | 73 | from ._version import __version__ 74 | 75 | 76 | PROJECTQ_OPERATION_MAP = { 77 | # native PennyLane operations also native to ProjectQ 78 | "PauliX": XGate, 79 | "PauliY": YGate, 80 | "PauliZ": ZGate, 81 | "CNOT": CNOT, 82 | "CZ": CZ, 83 | "SWAP": SwapGate, 84 | "RX": Rx, 85 | "RY": Ry, 86 | "RZ": Rz, 87 | "PhaseShift": R, 88 | "Hadamard": HGate, 89 | # operations not natively implemented in ProjectQ but provided in pqops.py 90 | "Rot": Rot, 91 | "QubitUnitary": QubitUnitary, 92 | "BasisState": BasisState, 93 | "S": SGate, 94 | "T": TGate, 95 | # additional operations not native to PennyLane but present in ProjectQ 96 | "SqrtX": SqrtXGate, 97 | "SqrtSwap": SqrtSwapGate, 98 | # operations/expectations of ProjectQ that do not work with PennyLane 99 | #'AllPauliZ': AllZGate, #todo: enable when multiple return values are supported 100 | # operations/expectations of PennyLane that do not work with ProjectQ 101 | #'QubitStateVector': StatePreparation, 102 | # In addition we support the Identity Expectation, but only as an expectation and not as an Operation, which is we we don't put it here. 103 | } 104 | 105 | 106 | class _ProjectQDevice(Device): # pylint: disable=abstract-method 107 | """ProjectQ device for PennyLane. 108 | 109 | Args: 110 | wires (int or Iterable[Number, str]]): Number of subsystems represented by the device, 111 | or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) 112 | or strings (``['ancilla', 'q1', 'q2']``). Default 1 if not specified. 113 | shots (None, int): How many times the circuit should be evaluated (or sampled) to estimate 114 | the expectation values. Defaults to ``None`` if not specified, which means that the device 115 | returns analytical results. 116 | 117 | Keyword Args: 118 | backend (string): Name of the backend, i.e., either "Simulator", 119 | "ClassicalSimulator", or "IBMBackend". 120 | verbose (bool): If True, log messages are printed and exceptions are more verbose. 121 | 122 | Keyword Args for Simulator backend: 123 | gate_fusion (bool): If True, operations are cached and only executed once a 124 | certain number of operations has been reached (only has an effect for the c++ simulator). 125 | rnd_seed (int): Random seed (uses random.randint(0, 4294967295) by default). 126 | 127 | Keyword Args for IBMBackend backend: 128 | use_hardware (bool): If True, the code is run on the IBM quantum chip (instead of using 129 | the IBM simulator) 130 | num_runs (int): Number of runs to collect statistics (default is 1024). Is equivalent 131 | to but takes preference over the shots parameter. 132 | user (string): IBM Quantum Experience user name 133 | password (string): IBM Quantum Experience password 134 | device (string): Device to use (e.g., ‘ibmqx4’ or ‘ibmqx5’) if use_hardware is set to 135 | True. Default is ibmqx4. 136 | retrieve_execution (int): Job ID to retrieve instead of re-running the circuit 137 | (e.g., if previous run timed out). 138 | """ 139 | 140 | name = "ProjectQ PennyLane plugin" 141 | short_name = "projectq" 142 | pennylane_requires = ">=0.15.0" 143 | version = "0.4.2" 144 | plugin_version = __version__ 145 | author = "Christian Gogolin and Xanadu" 146 | _capabilities = { 147 | "backend": list(["Simulator", "ClassicalSimulator", "IBMBackend"]), 148 | "model": "qubit", 149 | } 150 | 151 | @property 152 | def _operation_map(self): 153 | raise NotImplementedError 154 | 155 | @property 156 | def _observable_map(self): 157 | raise NotImplementedError 158 | 159 | @property 160 | def _backend_kwargs(self): 161 | raise NotImplementedError 162 | 163 | def __init__(self, wires=1, shots=None, *, backend, **kwargs): 164 | # overwrite shots with num_runs if given 165 | if "num_runs" in kwargs: 166 | shots = kwargs["num_runs"] 167 | del kwargs["num_runs"] 168 | 169 | super().__init__(wires=wires, shots=shots) 170 | 171 | if "verbose" not in kwargs: 172 | kwargs["verbose"] = False 173 | 174 | self._backend = backend 175 | self._kwargs = kwargs 176 | self._eng = None 177 | self._reg = None 178 | self._first_operation = True 179 | self.reset() # the actual initialization is done in reset() 180 | 181 | def reset(self): 182 | """Reset/initialize the device by allocating qubits.""" 183 | self._reg = self._eng.allocate_qureg(self.num_wires) 184 | self._first_operation = True 185 | 186 | def __repr__(self): 187 | return super().__repr__() + "Backend: " + self._backend + "\n" 188 | 189 | def __str__(self): 190 | return super().__str__() + "Backend: " + self._backend + "\n" 191 | 192 | def post_measure(self): 193 | """Deallocate the qubits after expectation values have been retrieved.""" 194 | self._deallocate() 195 | 196 | def apply(self, operation, wires, par): 197 | """Apply a quantum operation. 198 | 199 | For plugin developers: this function should apply the operation on the device. 200 | 201 | Args: 202 | operation (str): name of the operation 203 | wires (Sequence[int]): subsystems the operation is applied on 204 | par (tuple): parameters for the operation 205 | """ 206 | operation = self._operation_map[operation](*par) 207 | if isinstance(operation, BasisState) and not self._first_operation: 208 | raise DeviceError( 209 | "Operation {} cannot be used after other Operations have already " 210 | "been applied on a {} device.".format(operation, self.short_name) 211 | ) 212 | self._first_operation = False 213 | 214 | # translate wires to reflect labels on the device 215 | device_wires = self.map_wires(wires) 216 | 217 | qureg = [self._reg[i] for i in device_wires.labels] 218 | if isinstance( 219 | operation, 220 | ( 221 | pq.ops._metagates.ControlledGate, # pylint: disable=protected-access 222 | pq.ops._gates.SqrtSwapGate, # pylint: disable=protected-access 223 | pq.ops._gates.SwapGate, # pylint: disable=protected-access 224 | ), 225 | ): # pylint: disable=protected-access 226 | qureg = tuple(qureg) 227 | operation | qureg # pylint: disable=pointless-statement 228 | 229 | def _deallocate(self): 230 | """Deallocate all qubits to make ProjectQ happy 231 | 232 | See also: https://github.com/ProjectQ-Framework/ProjectQ/issues/2 233 | 234 | Drawback: This is probably rather resource intensive. 235 | """ 236 | if self._eng is not None and self._backend == "Simulator": 237 | # avoid an "unfriendly error message": 238 | # https://github.com/ProjectQ-Framework/ProjectQ/issues/2 239 | pq.ops.All(pq.ops.Measure) | self._reg # pylint: disable=expression-not-assigned 240 | 241 | def filter_kwargs_for_backend(self, kwargs): 242 | """Filter the given kwargs for those relevant for the respective device/backend.""" 243 | return {key: value for key, value in kwargs.items() if key in self._backend_kwargs} 244 | 245 | @property 246 | def operations(self): 247 | """Get the supported set of operations. 248 | 249 | Returns: 250 | set[str]: the set of PennyLane operation names the device supports 251 | """ 252 | return set(self._operation_map.keys()) 253 | 254 | @property 255 | def observables(self): 256 | """Get the supported set of observables. 257 | 258 | Returns: 259 | set[str]: the set of PennyLane observable names the device supports 260 | """ 261 | return set(self._observable_map.keys()) 262 | 263 | 264 | class ProjectQSimulator(_ProjectQDevice): 265 | """A PennyLane :code:`projectq.simulator` device for the `ProjectQ Simulator 266 | `_ 267 | backend. 268 | 269 | Args: 270 | wires (int or Iterable[Number, str]]): Number of subsystems represented by the device, 271 | or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) 272 | or strings (``['ancilla', 'q1', 'q2']``). 273 | shots (None, int): How many times the circuit should be evaluated (or sampled) to estimate 274 | the expectation values. Defaults to ``None`` if not specified, which means that the device 275 | returns analytical results. 276 | 277 | Keyword Args: 278 | gate_fusion (bool): If True, operations are cached and only executed once a 279 | certain number of operations has been reached (only has an effect for the c++ simulator). 280 | rnd_seed (int): Random seed (uses random.randint(0, 4294967295) by default). 281 | verbose (bool): If True, log messages are printed and exceptions are more verbose. 282 | 283 | This device can, for example, be instantiated from PennyLane as follows: 284 | 285 | .. code-block:: python 286 | 287 | import pennylane as qml 288 | dev = qml.device('projectq.simulator', wires=XXX) 289 | 290 | Supported PennyLane Operations: 291 | :class:`pennylane.PauliX`, 292 | :class:`pennylane.PauliY`, 293 | :class:`pennylane.PauliZ`, 294 | :class:`pennylane.CNOT`, 295 | :class:`pennylane.CZ`, 296 | :class:`pennylane.SWAP`, 297 | :class:`pennylane.RX`, 298 | :class:`pennylane.RY`, 299 | :class:`pennylane.RZ`, 300 | :class:`pennylane.PhaseShift`, 301 | :class:`pennylane.Hadamard`, 302 | :class:`pennylane.Rot`, 303 | :class:`pennylane.QubitUnitary`, 304 | :class:`pennylane.BasisState`, 305 | :class:`pennylane_pq.S `, 306 | :class:`pennylane_pq.T `, 307 | 308 | Supported PennyLane observables: 309 | :class:`pennylane.PauliX`, 310 | :class:`pennylane.PauliY`, 311 | :class:`pennylane.PauliZ`, 312 | :class:`pennylane.Hadamard`, 313 | :class:`pennylane.Identity` 314 | 315 | Extra Operations: 316 | :class:`pennylane_pq.SqrtX `, 317 | :class:`pennylane_pq.SqrtSwap ` 318 | 319 | """ 320 | 321 | short_name = "projectq.simulator" 322 | _operation_map = PROJECTQ_OPERATION_MAP 323 | _observable_map = dict( 324 | {key: val for key, val in _operation_map.items() if val in [XGate, YGate, ZGate, HGate]}, 325 | **{"Identity": None} 326 | ) 327 | _circuits = {} 328 | _backend_kwargs = ["gate_fusion", "rnd_seed"] 329 | 330 | def __init__(self, wires=1, shots=None, **kwargs): 331 | kwargs["backend"] = "Simulator" 332 | super().__init__(wires=wires, shots=shots, **kwargs) 333 | 334 | def reset(self): 335 | """Reset/initialize the device by initializing the backend and engine, and allocating qubits.""" 336 | backend = pq.backends.Simulator(**self.filter_kwargs_for_backend(self._kwargs)) 337 | self._eng = pq.MainEngine(backend, verbose=self._kwargs["verbose"]) 338 | super().reset() 339 | 340 | def pre_measure(self): 341 | """Flush the device before retrieving observable measurements.""" 342 | self._eng.flush(deallocate_qubits=False) 343 | 344 | def expval(self, observable, wires, par): 345 | """Retrieve the requested observable expectation value.""" 346 | device_wires = self.map_wires(wires) 347 | 348 | if observable in ["PauliX", "PauliY", "PauliZ"]: 349 | expval = self._eng.backend.get_expectation_value( 350 | pq.ops.QubitOperator(str(observable)[-1] + "0"), [self._reg[device_wires.labels[0]]] 351 | ) 352 | elif observable == "Hadamard": 353 | expval = self._eng.backend.get_expectation_value( 354 | 1 / np.sqrt(2) * pq.ops.QubitOperator("X0") 355 | + 1 / np.sqrt(2) * pq.ops.QubitOperator("Z0"), 356 | [self._reg[device_wires.labels[0]]], 357 | ) 358 | elif observable == "Identity": 359 | expval = 1 360 | # elif observable == 'AllPauliZ': 361 | # expval = [self._eng.backend.get_expectation_value( 362 | # pq.ops.QubitOperator("Z"+'0'), [qubit]) 363 | # for qubit in self._reg] 364 | 365 | if not self.shots is None and observable != "Identity": 366 | p0 = (expval + 1) / 2 367 | p0 = max(min(p0, 1), 0) 368 | n0 = np.random.binomial(self.shots, p0) 369 | expval = (n0 - (self.shots - n0)) / self.shots 370 | 371 | return expval 372 | 373 | def var(self, observable, wires, par): 374 | """Retrieve the requested observable variance.""" 375 | expval = self.expval(observable, wires, par) 376 | variance = 1 - expval**2 377 | # TODO: if this plugin supports non-involutory observables in future, may need to refactor this function 378 | return variance 379 | 380 | 381 | class ProjectQIBMBackend(_ProjectQDevice): 382 | """A PennyLane :code:`projectq.ibm` device for the `ProjectQ IBMBackend 383 | `_ 384 | backend. 385 | 386 | .. note:: 387 | This device computes expectation values by averaging over a 388 | finite number of runs of the quantum circuit. Irrespective of whether 389 | this is done on real quantum hardware, or on the IBM simulator, this 390 | means that expectation values (and therefore also gradients) will have 391 | a finite accuracy and fluctuate from run to run. 392 | 393 | Args: 394 | wires (int or Iterable[Number, str]]): Number of subsystems represented by the device, 395 | or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) 396 | or strings (``['ancilla', 'q1', 'q2']``). 397 | shots (int): number of circuit evaluations used to estimate expectation values 398 | of observables. Default value is 1024. 399 | 400 | Keyword Args: 401 | use_hardware (bool): If True, the code is run on the IBM quantum chip 402 | (instead of using the IBM simulator) 403 | num_runs (int): Number of runs to collect statistics (default is 1024). 404 | Is equivalent to but takes preference over the shots parameter. 405 | token (string): IBM Quantum Experience API token 406 | device (string): IBMQ backend to use (ibmq_16_melbourne’, ‘ibmqx2’, 'ibmq_rome' 'ibmq_qasm_simulator') if 407 | :code:`use_hardware` is set to True. Default is '‘ibmqx5’'. 408 | retrieve_execution (int): Job ID to retrieve instead of re-running 409 | a circuit (e.g., if previous run timed out). 410 | verbose (bool): If True, log messages are printed and exceptions are more verbose. 411 | 412 | This device can, for example, be instantiated from PennyLane as follows: 413 | 414 | .. code-block:: python 415 | 416 | import pennylane as qml 417 | dev = qml.device('projectq.ibm', wires=XXX, token="XXX") 418 | 419 | To avoid leaking your user name and password when sharing code, 420 | it is better to specify the user name and password in your 421 | `PennyLane configuration file `_. 422 | 423 | Supported PennyLane Operations: 424 | :class:`pennylane.PauliX`, 425 | :class:`pennylane.PauliY`, 426 | :class:`pennylane.PauliZ`, 427 | :class:`pennylane.CNOT`, 428 | :class:`pennylane.CZ`, 429 | :class:`pennylane.SWAP`, 430 | :class:`pennylane.RX`, 431 | :class:`pennylane.RY`, 432 | :class:`pennylane.RZ`, 433 | :class:`pennylane.PhaseShift`, 434 | :class:`pennylane.Hadamard`, 435 | :class:`pennylane.Rot`, 436 | :class:`pennylane.BasisState` 437 | 438 | Supported PennyLane observables: 439 | :class:`pennylane.PauliX`, 440 | :class:`pennylane.PauliY`, 441 | :class:`pennylane.PauliZ`, 442 | :class:`pennylane.Hadamard`, 443 | :class:`pennylane.Identity` 444 | 445 | .. note:: 446 | The observables :class:`pennylane.PauliY`, :class:`pennylane.PauliZ`, 447 | and :class:`pennylane.Hadamard`, cannot be natively measured on the 448 | hardware device. They are implemented by executing a few additional gates on the 449 | respective wire before the final measurement, which is always performed in the 450 | :class:`pennylane.PauliZ` basis. These measurements may thus be slightly more 451 | noisy than native :class:`pennylane.PauliZ` measurement. 452 | 453 | 454 | Extra Operations: 455 | :class:`pennylane_pq.S `, 456 | :class:`pennylane_pq.T `, 457 | :class:`pennylane_pq.SqrtX `, 458 | :class:`pennylane_pq.SqrtSwap `, 459 | 460 | """ 461 | 462 | short_name = "projectq.ibm" 463 | _operation_map = { 464 | key: val 465 | for key, val in PROJECTQ_OPERATION_MAP.items() 466 | if val 467 | in [ 468 | HGate, 469 | XGate, 470 | YGate, 471 | ZGate, 472 | SGate, 473 | TGate, 474 | SqrtXGate, 475 | SwapGate, 476 | SqrtSwapGate, 477 | Rx, 478 | Ry, 479 | Rz, 480 | R, 481 | CNOT, 482 | CZ, 483 | Rot, 484 | BasisState, 485 | ] 486 | } 487 | _observable_map = dict( 488 | {key: val for key, val in _operation_map.items() if val in [HGate, XGate, YGate, ZGate]}, 489 | **{"Identity": None} 490 | ) 491 | _circuits = {} 492 | _backend_kwargs = [ 493 | "use_hardware", 494 | "num_runs", 495 | "verbose", 496 | "token", 497 | "device", 498 | "retrieve_execution", 499 | ] 500 | 501 | def __init__(self, wires=1, shots=1024, **kwargs): 502 | # check that necessary arguments are given 503 | if "token" not in kwargs: 504 | raise ValueError( 505 | 'An IBM Quantum Experience token specified via the "token" keyword argument is required' 506 | ) # pylint: disable=line-too-long 507 | 508 | kwargs["backend"] = "IBMBackend" 509 | super().__init__(wires=wires, shots=shots, **kwargs) 510 | 511 | def reset(self): 512 | """Reset/initialize the device by initializing the backend and engine, and allocating qubits.""" 513 | backend = pq.backends.IBMBackend( 514 | num_runs=self.shots, **self.filter_kwargs_for_backend(self._kwargs) 515 | ) 516 | token = self._kwargs.get("token", "") 517 | hw = self._kwargs.get("use_hardware", False) 518 | device = self._kwargs.get("device", "ibmq_qasm_simulator" if not hw else "ibmqx2") 519 | self._eng = pq.MainEngine( 520 | backend, 521 | verbose=self._kwargs["verbose"], 522 | engine_list=get_engine_list(token=token, device=device), 523 | ) 524 | super().reset() 525 | 526 | def pre_measure(self): 527 | """Rotate qubits to the right basis before measurement, apply a measure all 528 | operation and flush the device before retrieving expectation values. 529 | """ 530 | if hasattr( 531 | self, "obs_queue" 532 | ): # we raise an except below in case there is no obs_queue but we are asked to measure in a basis different from PauliZ 533 | for obs in self.obs_queue: 534 | if obs.name == "PauliX": 535 | self.apply("Hadamard", obs.wires, list()) 536 | elif obs.name == "PauliY": 537 | self.apply("PauliZ", obs.wires, list()) 538 | self.apply("S", obs.wires, list()) 539 | self.apply("Hadamard", obs.wires, list()) 540 | elif obs.name == "Hadamard": 541 | self.apply("RY", obs.wires, [-np.pi / 4]) 542 | elif obs.name == "Hermitian": 543 | raise NotImplementedError 544 | 545 | pq.ops.All(pq.ops.Measure) | self._reg # pylint: disable=expression-not-assigned 546 | self._eng.flush() 547 | 548 | def expval(self, observable, wires, par): 549 | """Retrieve the requested observable expectation value.""" 550 | 551 | device_wires = self.map_wires(wires) 552 | 553 | probabilities = self._eng.backend.get_probabilities(self._reg) 554 | 555 | if observable in ["PauliX", "PauliY", "PauliZ", "Hadamard"]: 556 | if observable != "PauliZ" and not hasattr(self, "obs_queue"): 557 | raise DeviceError( 558 | "Measurements in basis other than PauliZ are only supported when " 559 | "this plugin is used with versions of PennyLane that expose the obs_queue. " 560 | "Please update PennyLane and this plugin." 561 | ) 562 | 563 | expval = ( 564 | 1 565 | - ( 566 | 2 567 | * sum( 568 | p 569 | for (state, p) in probabilities.items() 570 | if state[device_wires.labels[0]] == "1" 571 | ) 572 | ) 573 | - ( 574 | 1 575 | - 2 576 | * sum( 577 | p 578 | for (state, p) in probabilities.items() 579 | if state[device_wires.labels[0]] == "0" 580 | ) 581 | ) 582 | ) / 2 583 | 584 | elif observable == "Hermitian": 585 | raise NotImplementedError 586 | 587 | elif observable == "Identity": 588 | expval = sum(p for (state, p) in probabilities.items()) 589 | # elif observable == 'AllPauliZ': 590 | # expval = [((1-2*sum(p for (state, p) in probabilities.items() 591 | # if state[i] == '1')) 592 | # -(1-2*sum(p for (state, p) in probabilities.items() 593 | # if state[i] == '0')))/2 for i in range(len(self._reg))] 594 | 595 | return expval 596 | 597 | def var(self, observable, wires, par): 598 | """Retrieve the requested observable variance.""" 599 | expval = self.expval(observable, wires, par) 600 | variance = 1 - expval**2 601 | # TODO: if this plugin supports non-involutory observables in future, may need to refactor this function 602 | return variance 603 | 604 | 605 | class ProjectQClassicalSimulator(_ProjectQDevice): 606 | """A PennyLane :code:`projectq.classical` device for the `ProjectQ ClassicalSimulator 607 | `_ 608 | backend. 609 | 610 | Args: 611 | wires (int or Iterable[Number, str]]): Number of subsystems represented by the device, 612 | or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) 613 | or strings (``['ancilla', 'q1', 'q2']``). 614 | 615 | 616 | Keyword Args: 617 | verbose (bool): If True, log messages are printed and exceptions are more verbose. 618 | 619 | This device can, for example, be instantiated from PennyLane as follows: 620 | 621 | .. code-block:: python 622 | 623 | import pennylane as qml 624 | dev = qml.device('projectq.classical', wires=XXX) 625 | 626 | Supported PennyLane Operations: 627 | :class:`pennylane.PauliX`, 628 | :class:`pennylane.CNOT`, 629 | :class:`pennylane.BasisState` 630 | 631 | Supported PennyLane observables: 632 | :class:`pennylane.PauliZ`, 633 | :class:`pennylane.Identity` 634 | 635 | """ 636 | 637 | short_name = "projectq.classical" 638 | _operation_map = { 639 | key: val for key, val in PROJECTQ_OPERATION_MAP.items() if val in [XGate, CNOT, BasisState] 640 | } 641 | _observable_map = dict( 642 | {key: val for key, val in PROJECTQ_OPERATION_MAP.items() if val in [ZGate]}, 643 | **{"Identity": None} 644 | ) 645 | _circuits = {} 646 | _backend_kwargs = [] 647 | 648 | def __init__(self, wires=1, **kwargs): 649 | kwargs["backend"] = "ClassicalSimulator" 650 | super().__init__(wires=wires, shots=None, **kwargs) 651 | 652 | def reset(self): 653 | """Reset/initialize the device by initializing the backend and engine, and allocating qubits.""" 654 | backend = pq.backends.ClassicalSimulator(**self.filter_kwargs_for_backend(self._kwargs)) 655 | self._eng = pq.MainEngine(backend, verbose=self._kwargs["verbose"]) 656 | super().reset() 657 | 658 | def pre_measure(self): 659 | """Apply a measure all operation and flush the device before retrieving observable measurements.""" 660 | pq.ops.All(pq.ops.Measure) | self._reg # pylint: disable=expression-not-assigned 661 | self._eng.flush() 662 | 663 | def expval(self, observable, wires, par): 664 | """Retrieve the requested observable expectation values.""" 665 | 666 | device_wires = self.map_wires(wires) 667 | 668 | if observable == "PauliZ": 669 | wire = device_wires.labels[0] 670 | expval = 1 - 2 * int(self._reg[wire]) 671 | 672 | elif observable == "Identity": 673 | expval = 1 674 | # elif observable == 'AllPauliZ': 675 | # expval = [ 1 - 2*int(self._reg[wire]) for wire in self._reg] 676 | 677 | return expval 678 | 679 | def var(self, observable, wires, par): 680 | """Retrieve the requested observable variance.""" 681 | expval = self.expval(observable, wires, par) 682 | variance = 1 - expval**2 683 | # TODO: if this plugin supports non-involutory observables in future, may need to refactor this function 684 | return variance 685 | -------------------------------------------------------------------------------- /pennylane_pq/expval.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | r""" 15 | Expectations 16 | ############ 17 | 18 | .. currentmodule:: pennylane_pq.expval 19 | 20 | In addition to the suitable default operations native to PennyLane, 21 | the devices of the ProjectQ plugin support a number of additional expectations 22 | that can be used alongside the native PennyLane expectations when defining 23 | quantum functions: 24 | 25 | .. autosummary:: 26 | .. AllPauliZ 27 | """ 28 | 29 | # from pennylane.operation import Expectation 30 | 31 | # class AllPauliZ(Expectation): 32 | # r"""Measure Pauli Z on all qubits. 33 | 34 | # .. math:: AllPauliZ = \sigma_z \otimes\dots\otimes \sigma_z 35 | 36 | # """ 37 | # num_params = 0 38 | # num_wires = 0 39 | # par_domain = None 40 | -------------------------------------------------------------------------------- /pennylane_pq/ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | r""" 15 | Operations 16 | ########## 17 | 18 | .. currentmodule:: pennylane_pq.ops 19 | 20 | In addition to the suitable default operations native to PennyLane, 21 | the devices of the ProjectQ plugin support a number of additional operations 22 | that can be used alongside the native PennyLane operations when defining 23 | quantum functions: 24 | 25 | .. autosummary:: 26 | SqrtX 27 | SqrtSwap 28 | .. AllPauliZ 29 | 30 | .. note:: 31 | For convenience, and to mirror the behavior of the operations built into 32 | PennyLane, the operations defined here are also accessible directly under 33 | the top-level :code:`pennylane_pq` context, i.e., you can use 34 | :code:`pennylane_pq.SqrtX([0])` instead of :code:`pennylane_pq.ops.SqrtX([0])` 35 | when defining a :code:`QNode` using the :code:`qnode` decorator. 36 | 37 | """ 38 | 39 | import pennylane.operation as plops 40 | 41 | 42 | class SqrtX(plops.Operation): 43 | r"""Square root X gate. 44 | 45 | .. math:: \mathrm{SqrtX} = \begin{bmatrix}1+i&1-i\\1-i&1+i\end{bmatrix} 46 | 47 | Args: 48 | wires (int): the subsystem the gate acts on 49 | """ 50 | num_params = 0 51 | num_wires = 1 52 | par_domain = None 53 | 54 | 55 | class SqrtSwap(plops.Operation): # pylint: disable=too-few-public-methods 56 | r"""Square root SWAP gate. 57 | 58 | .. math:: \mathrm{SqrtSwap} = \begin{bmatrix}1&0&0&0\\0&(1+i)/2&(1-i)/2&0\\ 59 | 0&(1-i)/2 &(1+i)/2&0\\0&0&0&1\end{bmatrix} 60 | 61 | Args: 62 | wires (seq[int]): the subsystems the gate acts on 63 | """ 64 | num_params = 0 65 | num_wires = 2 66 | par_domain = None 67 | 68 | 69 | # class Toffoli(Operation): #pylint: disable=too-few-public-methods 70 | # r"""Apply the Tofoli gate. 71 | # """ 72 | # num_params = 0 73 | # num_wires = 3 74 | # par_domain = None 75 | 76 | # class AllPauliZ(Operation): 77 | # r"""Apply Pauli Z to all wires. 78 | 79 | # .. math:: AllPauliZ = \sigma_z \otimes\dots\otimes \sigma_z 80 | 81 | # .. todo:: Potentially remove this gate depending on how 82 | # https://github.com/PennyLaneAI/pennylane/issues/61 is resolved. 83 | 84 | # """ 85 | # num_params = 0 86 | # num_wires = 0 87 | # par_domain = None 88 | -------------------------------------------------------------------------------- /pennylane_pq/pqops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # pylint: disable=expression-not-assigned 15 | r""" 16 | Wrapper classes for ProjectQ Operations 17 | =================== 18 | 19 | .. currentmodule:: pennylane_pq.ops 20 | 21 | This module provides wrapper classes for `Operations` that are missing a class in ProjectQ. 22 | 23 | """ 24 | import projectq as pq 25 | from projectq.ops import BasicGate, SelfInverseGate 26 | import numpy as np 27 | 28 | 29 | class BasicProjectQGate(BasicGate): # pylint: disable=too-few-public-methods 30 | """Base class for ProjectQ gates.""" 31 | 32 | def __init__(self, name="unnamed"): 33 | super().__init__() 34 | self.name = name 35 | 36 | def __str__(self): 37 | return self.name 38 | 39 | 40 | try: 41 | from projectq.ops import MatrixGate # pylint: disable=ungrouped-imports 42 | except ImportError: 43 | MatrixGate = BasicGate 44 | 45 | 46 | class BasicProjectQMatrixGate(MatrixGate): # pylint: disable=too-few-public-methods 47 | """Base class for ProjectQ gates defined via a matrix.""" 48 | 49 | def __init__(self, name="unnamed"): 50 | super().__init__() 51 | self.name = name 52 | 53 | def __str__(self): 54 | return self.name 55 | 56 | 57 | class CNOT(BasicProjectQGate): # pylint: disable=too-few-public-methods 58 | """Class for the CNOT gate. 59 | 60 | Contrary to other gates, ProjectQ does not have a class for the CNOT gate, 61 | as it is implemented as a meta-gate. 62 | For consistency we define this class, whose constructor is made to retun 63 | a gate with the correct properties by overwriting __new__(). 64 | """ 65 | 66 | def __new__(*par): # pylint: disable=no-method-argument 67 | return pq.ops.C(pq.ops.XGate()) 68 | 69 | 70 | class CZ(BasicProjectQGate): # pylint: disable=too-few-public-methods 71 | """Class for the CNOT gate. 72 | 73 | Contrary to other gates, ProjectQ does not have a class for the CZ gate, 74 | as it is implemented as a meta-gate. 75 | For consistency we define this class, whose constructor is made to retun 76 | a gate with the correct properties by overwriting __new__(). 77 | """ 78 | 79 | def __new__(*par): # pylint: disable=no-method-argument 80 | return pq.ops.C(pq.ops.ZGate()) 81 | 82 | 83 | # class Toffoli(BasicProjectQGate): # pylint: disable=too-few-public-methods 84 | # """Class for the Toffoli gate. 85 | 86 | # Contrary to other gates, ProjectQ does not have a class for the Toffoli gate, 87 | # as it is implemented as a meta-gate. 88 | # For consistency we define this class, whose constructor is made to retun 89 | # a gate with the correct properties by overwriting __new__(). 90 | # """ 91 | # def __new__(*par): # pylint: disable=no-method-argument 92 | # return pq.ops.C(pq.ops.ZGate(), 2) 93 | 94 | # class AllZGate(BasicProjectQGate): # pylint: disable=too-few-public-methods 95 | # """Class for the AllZ gate. 96 | 97 | # Contrary to other gates, ProjectQ does not have a class for the AllZ gate, 98 | # as it is implemented as a meta-gate. 99 | # For consistency we define this class, whose constructor is made to retun 100 | # a gate with the correct properties by overwriting __new__(). 101 | # """ 102 | # def __new__(*par): # pylint: disable=no-method-argument 103 | # return pq.ops.Tensor(pq.ops.ZGate()) 104 | 105 | 106 | class Rot(BasicProjectQGate): 107 | """Class for the arbitrary single qubit rotation gate. 108 | 109 | ProjectQ does not currently have an arbitrary single qubit rotation gate, 110 | so we provide a class that return a suitable combination of rotation gates 111 | assembled into a single gate from the constructor of this class. 112 | """ 113 | 114 | def __init__(self, *par): 115 | BasicProjectQGate.__init__(self, name=self.__class__.__name__) 116 | self.angles = par 117 | 118 | def __or__(self, qubits): 119 | pq.ops.Rz(self.angles[0]) | qubits # pylint: disable=expression-not-assigned 120 | pq.ops.Ry(self.angles[1]) | qubits # pylint: disable=expression-not-assigned 121 | pq.ops.Rz(self.angles[2]) | qubits # pylint: disable=expression-not-assigned 122 | 123 | def __eq__(self, other): 124 | if isinstance(other, self.__class__): 125 | return self.angles == other.angles 126 | return False 127 | 128 | 129 | class QubitUnitary(BasicProjectQGate): # pylint: disable=too-few-public-methods 130 | """Class for the QubitUnitary gate. 131 | 132 | ProjectQ does not currently have a real arbitrary QubitUnitary gate, 133 | but it allows to directly set the matrix of single qubit gates and 134 | can then still decompose them into the elementary gates set, so we 135 | do this here. 136 | """ 137 | 138 | def __new__(*par): 139 | unitary_gate = BasicProjectQMatrixGate(par[0].__name__) 140 | unitary_gate.matrix = np.array(par[1]) 141 | return unitary_gate 142 | 143 | 144 | class BasisState(BasicProjectQGate, SelfInverseGate): # pylint: disable=too-few-public-methods 145 | """Class for the BasisState preparation. 146 | 147 | ProjectQ does not currently have a dedicated gate for this, so we implement it here. 148 | """ 149 | 150 | def __init__(self, basis_state_to_prep): 151 | BasicProjectQGate.__init__(self, name=self.__class__.__name__) 152 | SelfInverseGate.__init__(self) 153 | self.basis_state_to_prep = basis_state_to_prep 154 | 155 | def __or__(self, qubits): 156 | for i, qureg in enumerate(qubits): 157 | if self.basis_state_to_prep[i] == 1: 158 | pq.ops.XGate() | qureg # pylint: disable=expression-not-assigned 159 | 160 | def __eq__(self, other): 161 | if isinstance(other, self.__class__): 162 | return self.basis_state_to_prep == other.basis_state_to_prep 163 | return False 164 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | projectq>=0.5.1 2 | pennylane>=0.15,<0.35 3 | pybind11 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #!/usr/bin/env python3 16 | from setuptools import setup 17 | 18 | with open("pennylane_pq/_version.py") as f: 19 | version = f.readlines()[-1].split()[-1].strip("\"'") # pylint: disable=invalid-name 20 | 21 | 22 | requirements = [ 23 | "projectq>=0.5.1", 24 | "pennylane>=0.15,<0.35" 25 | ] # pylint: disable=invalid-name 26 | 27 | 28 | info = { # pylint: disable=invalid-name 29 | "name": "PennyLane-PQ", 30 | "version": version, 31 | "maintainer": "Xanadu Inc.", 32 | "maintainer_email": "software@xanadu.ai", 33 | "url": "https://github.com/PennyLaneAI/PennyLane-PQ", 34 | "license": "Apache License 2.0", 35 | "packages": ["pennylane_pq"], 36 | "entry_points": { 37 | "pennylane.plugins": [ 38 | "projectq.simulator = pennylane_pq:ProjectQSimulator", 39 | "projectq.classical = pennylane_pq:ProjectQClassicalSimulator", 40 | "projectq.ibm = pennylane_pq:ProjectQIBMBackend", 41 | ] 42 | }, 43 | "description": "PennyLane plugin for ProjectQ", 44 | "long_description": open("README.rst").read(), 45 | 'long_description_content_type': "text/x-rst", 46 | "provides": ["pennylane_pq"], 47 | "install_requires": requirements, 48 | } 49 | 50 | classifiers = [ # pylint: disable=invalid-name 51 | "Development Status :: 4 - Beta", 52 | "Environment :: Console", 53 | "Intended Audience :: Science/Research", 54 | "License :: OSI Approved :: Apache Software License", 55 | "Natural Language :: English", 56 | "Operating System :: POSIX", 57 | "Operating System :: MacOS :: MacOS X", 58 | "Operating System :: POSIX :: Linux", 59 | "Operating System :: Microsoft :: Windows", 60 | "Programming Language :: Python", 61 | "Programming Language :: Python :: 3", 62 | "Programming Language :: Python :: 3.9", 63 | "Programming Language :: Python :: 3.10", 64 | "Programming Language :: Python :: 3.11", 65 | "Programming Language :: Python :: 3 :: Only", 66 | "Topic :: Scientific/Engineering :: Physics", 67 | ] 68 | 69 | setup(classifiers=classifiers, **(info)) 70 | -------------------------------------------------------------------------------- /tests/defaults.py: -------------------------------------------------------------------------------- 1 | """ 2 | Default parameters, commandline arguments and common routines for the unit tests. 3 | """ 4 | import unittest 5 | import os 6 | import sys 7 | import logging 8 | import argparse 9 | 10 | import pennylane 11 | from pennylane import numpy as np 12 | 13 | # Make sure pennylane_pq is always imported from the same source distribution 14 | # where the tests reside, not e.g. from site-packages. 15 | # See https://docs.python-guide.org/en/latest/writing/structure/#test-suite 16 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 17 | import pennylane_pq #pylint: disable=wrong-import-position,unused-import 18 | 19 | # defaults 20 | if 'DEVICE' in os.environ and os.environ['DEVICE'] is not None: 21 | DEVICE = os.environ['DEVICE'] 22 | else: 23 | DEVICE = "all" 24 | OPTIMIZER = "GradientDescentOptimizer" 25 | if DEVICE == "all" or DEVICE == "ibm": 26 | TOLERANCE = 5e-2 27 | else: 28 | TOLERANCE = 1e-3 29 | 30 | 31 | # set up logging 32 | if "LOGGING" in os.environ: 33 | logLevel = os.environ["LOGGING"] #pylint: disable=invalid-name 34 | print('Logging:', logLevel) 35 | numeric_level = getattr(logging, logLevel.upper(), 10) #pylint: disable=invalid-name 36 | else: 37 | numeric_level = 100 #pylint: disable=invalid-name 38 | 39 | logging.getLogger().setLevel(numeric_level) 40 | logging.captureWarnings(True) 41 | 42 | def get_commandline_args(): 43 | """Parse the commandline arguments for the unit tests. 44 | If none are given (e.g. when the test is run as a module instead of a script), 45 | the defaults are used. 46 | 47 | Returns: 48 | argparse.Namespace: parsed arguments in a namespace container 49 | """ 50 | parser = argparse.ArgumentParser() 51 | parser.add_argument('-d', '--device', type=str, default=DEVICE, 52 | help='Device(s) to use for tests.', choices=['simulator', 'ibm', 'classical', 'all']) 53 | parser.add_argument('-t', '--tolerance', type=float, default=TOLERANCE, 54 | help='Numerical tolerance for equality tests.') 55 | parser.add_argument("--user", 56 | help="IBM Quantum Experience user name") 57 | parser.add_argument("--password", 58 | help="IBM Quantum Experience password") 59 | parser.add_argument("--optimizer", default=OPTIMIZER, choices=pennylane.optimize.__all__, 60 | help="optimizer to use") 61 | 62 | # HACK: We only parse known args to enable unittest test discovery without parsing errors. 63 | args, _ = parser.parse_known_args() 64 | return args 65 | 66 | # parse any possible commandline arguments 67 | ARGS = get_commandline_args() 68 | 69 | class BaseTest(unittest.TestCase): 70 | """ABC for tests. 71 | Encapsulates the user-given commandline parameters for the test run as class attributes. 72 | """ 73 | num_subsystems = None #: int: number of wires for the device, must be overridden by child classes 74 | 75 | def setUp(self): 76 | self.args = ARGS 77 | self.tol = self.args.tolerance 78 | 79 | def logTestName(self): 80 | logging.info('{}'.format(self.id())) 81 | 82 | def assertAllElementsAlmostEqual(self, l, delta, msg=None): 83 | l = list(l) 84 | first = l.pop() 85 | for value in l: 86 | self.assertAllAlmostEqual(first, value, delta, msg) 87 | 88 | def assertAllAlmostEqual(self, first, second, delta, msg=None): 89 | """ 90 | Like assertAlmostEqual, but works with arrays. All the corresponding elements have to be almost equal. 91 | """ 92 | if isinstance(first, tuple): 93 | # check each element of the tuple separately (needed for when the tuple elements are themselves batches) 94 | if np.all([np.all(first[idx] == second[idx]) for idx, _ in enumerate(first)]): 95 | return 96 | if np.all([np.all(np.abs(first[idx] - second[idx])) <= delta for idx, _ in enumerate(first)]): 97 | return 98 | else: 99 | if np.all(first == second): 100 | return 101 | if np.all(np.abs(first - second) <= delta): 102 | return 103 | standardMsg = '{} != {} within {} delta'.format(first, second, delta) 104 | msg = self._formatMessage(msg, standardMsg) 105 | raise self.failureException(msg) 106 | 107 | 108 | def assertAllEqual(self, first, second, msg=None): 109 | """ 110 | Like assertEqual, but works with arrays. All the corresponding elements have to be equal. 111 | """ 112 | return self.assertAllAlmostEqual(first, second, delta=0.0, msg=msg) 113 | 114 | def assertAllTrue(self, value, msg=None): 115 | """ 116 | Like assertTrue, but works with arrays. All the corresponding elements have to be True. 117 | """ 118 | return self.assertTrue(np.all(value)) 119 | -------------------------------------------------------------------------------- /tests/test_basis_state.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` BasisState operation. 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | from defaults import pennylane as qml, BaseTest 21 | import pennylane 22 | from pennylane import numpy as np 23 | from pennylane_pq.devices import ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend 24 | 25 | log.getLogger('defaults') 26 | 27 | 28 | class BasisStateTest(BaseTest): 29 | """test the BasisState operation. 30 | """ 31 | 32 | num_subsystems = 4 33 | devices = None 34 | 35 | def setUp(self): 36 | super().setUp() 37 | 38 | self.devices = [] 39 | if self.args.device == 'simulator' or self.args.device == 'all': 40 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems, verbose=True)) 41 | if self.args.device == 'ibm' or self.args.device == 'all': 42 | ibm_options = pennylane.default_config['projectq.ibm'] 43 | if "token" in ibm_options: 44 | self.devices.append(ProjectQIBMBackend(wires=self.num_subsystems, use_hardware=False, num_runs=8*1024, 45 | token=ibm_options['token'], verbose=True)) 46 | else: 47 | log.warning("Skipping test of the ProjectQIBMBackend device because IBM login " 48 | "credentials could not be found in the PennyLane configuration file.") 49 | if self.args.device == 'classical' or self.args.device == 'all': 50 | self.devices.append(ProjectQClassicalSimulator(wires=self.num_subsystems, verbose=True)) 51 | 52 | def test_basis_state(self): 53 | """Test BasisState with preparations on the whole system.""" 54 | if self.devices is None: 55 | return 56 | self.logTestName() 57 | 58 | for device in self.devices: 59 | for bits_to_flip in [np.array([0, 0, 0, 0]), 60 | np.array([0, 1, 1, 0]), 61 | np.array([1, 1, 1, 0]), 62 | np.array([1, 1, 1, 1])]: 63 | @qml.qnode(device) 64 | def circuit(): 65 | qml.BasisState(bits_to_flip, wires=list(range(self.num_subsystems))) 66 | return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2)), qml.expval(qml.PauliZ(3)) 67 | 68 | self.assertAllAlmostEqual([1]*self.num_subsystems-2*bits_to_flip, np.array(circuit()), delta=self.tol) 69 | 70 | def test_basis_state_on_subsystem(self): 71 | """Test BasisState with preparations on subsystems.""" 72 | if self.devices is None: 73 | return 74 | self.logTestName() 75 | 76 | for device in self.devices: 77 | for bits_to_flip in [np.array([0, 0, 0]), 78 | np.array([1, 0, 0]), 79 | np.array([0, 1, 1]), 80 | np.array([1, 1, 0]), 81 | np.array([1, 1, 1])]: 82 | @qml.qnode(device) 83 | def circuit(): 84 | qml.BasisState(bits_to_flip, wires=list(range(self.num_subsystems-1))) 85 | return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2)), qml.expval(qml.PauliZ(3)) 86 | 87 | self.assertAllAlmostEqual([1]*(self.num_subsystems-1)-2*bits_to_flip, np.array(circuit()[:-1]), delta=self.tol) 88 | 89 | def test_basis_state_after_other_operation(self): 90 | if self.devices is None: 91 | return 92 | self.logTestName() 93 | 94 | if int(qml.__version__[3]) < 2: 95 | self.skipTest("mid circuit measurements not yet supported.") 96 | 97 | for device in self.devices: 98 | 99 | @qml.qnode(device) 100 | def circuit(): 101 | qml.PauliX(wires=[0]) 102 | qml.BasisState(np.array([1, 1, 0, 1]), wires=list(range(self.num_subsystems))) 103 | return qml.expval(qml.PauliZ(0)) 104 | 105 | assert qml.math.allclose(circuit(), 1) 106 | 107 | 108 | if __name__ == '__main__': 109 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', BasisState operation.') 110 | # run the tests in this file 111 | suite = unittest.TestSuite() 112 | for t in (BasisStateTest, ): 113 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 114 | suite.addTests(ttt) 115 | 116 | unittest.TextTestRunner().run(suite) 117 | -------------------------------------------------------------------------------- /tests/test_compare_with_default_qubit.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` devices. 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | from defaults import pennylane as qml, BaseTest 21 | from pennylane import numpy as np 22 | import pennylane 23 | from pennylane_pq.ops import SqrtSwap, SqrtX 24 | from pennylane_pq.devices import ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend 25 | 26 | log.getLogger('defaults') 27 | 28 | 29 | class CompareWithDefaultQubitTest(BaseTest): 30 | """Compares the behavior of the ProjectQ plugin devices with the default qubit device. 31 | """ 32 | num_subsystems = 4 # This should be as large as the largest gate/observable, but we cannot know that before instantiating the device. We thus check later that all gates/observables fit. 33 | 34 | devices = None 35 | 36 | def setUp(self): 37 | super().setUp() 38 | 39 | self.devices = [] 40 | if self.args.device == 'simulator' or self.args.device == 'all': 41 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems, verbose=True)) 42 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems, shots=20000000, verbose=True)) 43 | if self.args.device == 'ibm' or self.args.device == 'all': 44 | ibm_options = pennylane.default_config['projectq.ibm'] 45 | 46 | if "token" in ibm_options: 47 | self.devices.append(ProjectQIBMBackend(wires=self.num_subsystems, use_hardware=False, num_runs=8 * 1024, 48 | token=ibm_options['token'], verbose=True)) 49 | else: 50 | log.warning("Skipping test of the ProjectQIBMBackend device because IBM login credentials " 51 | "could not be found in the PennyLane configuration file.") 52 | if self.args.device == 'classical' or self.args.device == 'all': 53 | self.devices.append(ProjectQClassicalSimulator(wires=self.num_subsystems, verbose=True)) 54 | 55 | def test_simple_circuits(self): 56 | 57 | default_qubit = qml.device('default.qubit', wires=4) 58 | 59 | for dev in self.devices: 60 | gates = [ 61 | qml.PauliX(wires=0), 62 | qml.PauliY(wires=1), 63 | qml.PauliZ(wires=2), 64 | qml.S(wires=3), 65 | qml.T(wires=0), 66 | qml.RX(2.3, wires=1), 67 | qml.RY(1.3, wires=2), 68 | qml.RZ(3.3, wires=3), 69 | qml.Hadamard(wires=0), 70 | qml.Rot(0.1, 0.2, 0.3, wires=1), 71 | qml.CRot(0.1, 0.2, 0.3, wires=[2, 3]), 72 | qml.Toffoli(wires=[0, 1, 2]), 73 | qml.SWAP(wires=[1, 2]), 74 | qml.CSWAP(wires=[1, 2, 3]), 75 | qml.U1(1.0, wires=0), 76 | qml.U2(1.0, 2.0, wires=2), 77 | qml.U3(1.0, 2.0, 3.0, wires=3), 78 | qml.CRX(0.1, wires=[1, 2]), 79 | qml.CRY(0.2, wires=[2, 3]), 80 | qml.CRZ(0.3, wires=[3, 1]), 81 | qml.CZ(wires=[2, 3]), 82 | qml.QubitUnitary(np.array([[1, 0], [0, 1]]), wires=2), 83 | ] 84 | 85 | layers = 3 86 | np.random.seed(1967) 87 | gates_per_layers = [np.random.permutation(gates).numpy() for _ in range(layers)] 88 | 89 | for obs in {qml.PauliX(wires=0), qml.PauliY(wires=0), qml.PauliZ(wires=0), qml.Identity(wires=0), qml.Hadamard(wires=0)}: 90 | if obs.name in dev.observables: 91 | def circuit(): 92 | """4-qubit circuit with layers of randomly selected gates and random connections for 93 | multi-qubit gates.""" 94 | qml.BasisState(np.array([1, 0, 0, 0]), wires=[0, 1, 2, 3]) 95 | for gates in gates_per_layers: 96 | for gate in gates: 97 | if gate.name in dev.operations: 98 | qml.apply(gate) 99 | return qml.expval(obs) 100 | 101 | qnode_default = qml.QNode(circuit, default_qubit) 102 | qnode = qml.QNode(circuit, dev) 103 | 104 | assert np.allclose(qnode(), qnode_default(), atol=1e-3) 105 | 106 | def test_projectq_ops(self): 107 | 108 | results = [-1.0, -1.0] 109 | for i, dev in enumerate(self.devices[1:3]): 110 | 111 | gates = [ 112 | qml.PauliX(wires=0), 113 | qml.PauliY(wires=1), 114 | qml.PauliZ(wires=2), 115 | SqrtX(wires=0), 116 | SqrtSwap(wires=[3, 0]), 117 | ] 118 | 119 | layers = 3 120 | np.random.seed(1967) 121 | gates_per_layers = [np.random.permutation(gates).numpy() for _ in range(layers)] 122 | 123 | def circuit(): 124 | """4-qubit circuit with layers of randomly selected gates.""" 125 | for gates in gates_per_layers: 126 | for gate in gates: 127 | if gate.name in dev.operations: 128 | qml.apply(gate) 129 | return qml.expval(qml.PauliZ(0)) 130 | 131 | qnode = qml.QNode(circuit, dev) 132 | assert np.allclose(qnode(), results[i], atol=1e-3) 133 | 134 | -------------------------------------------------------------------------------- /tests/test_device_initialization.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` device initialization 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | from defaults import pennylane as qml, BaseTest 21 | from pennylane import DeviceError 22 | from pennylane_pq.devices import ProjectQIBMBackend 23 | import os 24 | 25 | token = os.getenv("IBMQX_TOKEN") 26 | log.getLogger('defaults') 27 | 28 | 29 | class DeviceInitialization(BaseTest): 30 | """test aspects of the device initialization. 31 | """ 32 | 33 | num_subsystems = 4 34 | devices = None 35 | 36 | def test_ibm_no_token(self): 37 | if self.args.device == 'ibm' or self.args.device == 'all': 38 | self.assertRaises(ValueError, ProjectQIBMBackend, wires=self.num_subsystems, use_hardware=False) 39 | 40 | def test_shots(self): 41 | if self.args.device == 'ibm' or self.args.device == 'all': 42 | shots = 5 43 | dev1 = ProjectQIBMBackend(wires=self.num_subsystems, shots=shots, use_hardware=False, token=token, verbose=True) 44 | self.assertEqual(shots, dev1.shots) 45 | 46 | dev2 = ProjectQIBMBackend(wires=self.num_subsystems, num_runs=shots, use_hardware=False, token=token) 47 | self.assertEqual(shots, dev2.shots) 48 | 49 | dev2 = ProjectQIBMBackend(wires=self.num_subsystems, shots=shots+2, num_runs=shots, use_hardware=False, 50 | token=token) 51 | self.assertEqual(shots, dev2.shots) 52 | 53 | def test_initiatlization_via_pennylane(self): 54 | for short_name in [ 55 | 'projectq.simulator', 56 | 'projectq.classical', 57 | 'projectq.ibm' 58 | ]: 59 | try: 60 | dev = qml.device(short_name, wires=2, token=token, verbose=True) 61 | except DeviceError: 62 | raise Exception("This test is expected to fail until pennylane-pq is installed.") 63 | 64 | 65 | if __name__ == '__main__': 66 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', device initialization.') 67 | # run the tests in this file 68 | suite = unittest.TestSuite() 69 | for t in (DeviceInitialization, ): 70 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 71 | suite.addTests(ttt) 72 | 73 | unittest.TextTestRunner().run(suite) 74 | -------------------------------------------------------------------------------- /tests/test_docs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` device documentation 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | import re 21 | from defaults import pennylane as qml, BaseTest 22 | from pennylane_pq.devices import ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend 23 | import os 24 | 25 | token = os.getenv("IBMQX_TOKEN") 26 | log.getLogger('defaults') 27 | 28 | 29 | class DocumentationTest(BaseTest): 30 | """test documentation of the plugin. 31 | """ 32 | 33 | num_subsystems = 4 34 | devices = None 35 | 36 | devices = None 37 | def setUp(self): 38 | super().setUp() 39 | 40 | self.devices = [] 41 | if self.args.device == 'simulator' or self.args.device == 'all': 42 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems)) 43 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems, shots=20000000)) 44 | if self.args.device == 'ibm' or self.args.device == 'all': 45 | self.devices.append(ProjectQIBMBackend(wires=self.num_subsystems, use_hardware=False, num_runs=8 * 1024, 46 | token=token, verbose=True)) 47 | if self.args.device == 'classical' or self.args.device == 'all': 48 | self.devices.append(ProjectQClassicalSimulator(wires=self.num_subsystems)) 49 | 50 | def test_device_docstrings(self): 51 | for dev in self.devices: 52 | docstring = dev.__doc__ 53 | supp_operations = dev.operations 54 | supp_observables = dev.observables 55 | #print(docstring) 56 | documented_operations = ([ re.findall(r"(?:pennylane\.|pennylane_pq.ops\.)([^`> ]*)", string) for string in re.findall(r"(?:(?:Extra|Supported PennyLane) Operations:\n((?:\s*:class:`[^`]+`,?\n)*))", docstring, re.MULTILINE)]) 57 | documented_operations = set([item for sublist in documented_operations for item in sublist]) 58 | 59 | documented_observables = ([ re.findall(r"(?:pennylane\.|pennylane_pq\.)([^`> ]*)", string) for string in re.findall(r"(?:(?:Extra|Supported PennyLane) observables:\n((?:\s*:class:`[^`]+`,?\n)*))", docstring, re.MULTILINE)]) 60 | documented_observables = set([item for sublist in documented_observables for item in sublist]) 61 | 62 | supported_but_not_documented_operations = supp_operations.difference(documented_operations) 63 | self.assertFalse(supported_but_not_documented_operations, msg="For device "+dev.short_name+" the Operations "+str(supported_but_not_documented_operations)+" are supported but not documented.") 64 | documented_but_not_supported_operations = documented_operations.difference(supp_operations) 65 | self.assertFalse(documented_but_not_supported_operations, msg="For device "+dev.short_name+" the Operations "+str(documented_but_not_supported_operations)+" are documented but not actually supported.") 66 | 67 | supported_but_not_documented_observables = supp_observables.difference(documented_observables) 68 | self.assertFalse(supported_but_not_documented_observables, msg="For device "+dev.short_name+" the Observables "+str(supported_but_not_documented_observables)+" are supported but not documented.") 69 | documented_but_not_supported_observables = documented_observables.difference(supp_observables) 70 | self.assertFalse(documented_but_not_supported_observables, msg="For device "+dev.short_name+" the Observables "+str(documented_but_not_supported_observables)+" are documented but not actually supported.") 71 | 72 | 73 | if __name__ == '__main__': 74 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', device documentation.') 75 | # run the tests in this file 76 | suite = unittest.TestSuite() 77 | for t in (DocumentationTest, ): 78 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 79 | suite.addTests(ttt) 80 | 81 | unittest.TextTestRunner().run(suite) 82 | -------------------------------------------------------------------------------- /tests/test_ibm_expval_and_pre_expval_mock.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` device documentation 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | from defaults import pennylane as qml, BaseTest 21 | from pennylane import DeviceError 22 | from pennylane.wires import Wires 23 | from pennylane_pq.devices import ProjectQIBMBackend 24 | from unittest.mock import patch, MagicMock, PropertyMock, call 25 | import os 26 | 27 | token = os.getenv("IBMQX_TOKEN") 28 | log.getLogger('defaults') 29 | 30 | 31 | class ExpvalAndPreExpvalMock(BaseTest): 32 | """test pre_measure and expval of the plugin in a fake way that works without ibm credentials 33 | """ 34 | 35 | def test_pre_measure(self): 36 | 37 | with patch('pennylane_pq.devices.ProjectQIBMBackend.obs_queue', new_callable=PropertyMock) as mock_obs_queue: 38 | mock_PauliX = MagicMock(wires=[0]) 39 | mock_PauliX.name = 'PauliX' 40 | mock_PauliY = MagicMock(wires=[0]) 41 | mock_PauliY.name = 'PauliY' 42 | mock_Hadamard = MagicMock(wires=[0]) 43 | mock_Hadamard.name = 'Hadamard' 44 | mock_Hermitian = MagicMock(wires=[0]) 45 | mock_Hermitian.name = 'Hermitian' 46 | 47 | mock_obs_queue.return_value = [ 48 | mock_PauliX, 49 | mock_PauliY, 50 | mock_Hadamard, 51 | ] 52 | dev = ProjectQIBMBackend(wires=2, use_hardware=False, num_runs=8*1024, token=token, verbose=True) 53 | dev._eng = MagicMock() 54 | dev.apply = MagicMock() 55 | 56 | with patch('projectq.ops.All', new_callable=PropertyMock) as mock_All: 57 | dev.pre_measure() 58 | 59 | dev._eng.assert_has_calls([call.flush()]) 60 | # The following might have to be changed in case a more elegant/efficient/different 61 | # implementation of the effective measurements is found 62 | dev.apply.assert_has_calls([call('Hadamard', [0], []), 63 | call('PauliZ', [0], []), 64 | call('S', [0], []), 65 | call('Hadamard', [0], []), 66 | call('RY', [0], [-0.7853981633974483])]) 67 | 68 | 69 | mock_obs_queue.return_value = [ 70 | mock_Hermitian 71 | ] 72 | with patch('projectq.ops.All', new_callable=PropertyMock) as mock_All: 73 | self.assertRaises(NotImplementedError, dev.pre_measure) 74 | 75 | def test_expval(self): 76 | 77 | dev = ProjectQIBMBackend(wires=2, use_hardware=False, num_runs=8*1024, token=token, verbose=True) 78 | dev._eng = MagicMock() 79 | dev._eng.backend = MagicMock() 80 | dev._eng.backend.get_probabilities = MagicMock() 81 | dev._eng.backend.get_probabilities.return_value = {'00': 0.1, '01': 0.3, '10': 0.2, '11': 0.4} 82 | 83 | self.assertAlmostEqual(dev.expval('PauliZ', wires=Wires([0]), par=list()), -0.2, delta=self.tol) 84 | self.assertAlmostEqual(dev.expval('Identity', wires=Wires([0]), par=list()), 1.0, delta=self.tol) 85 | self.assertRaises(NotImplementedError, dev.expval, 'Hermitian', wires=Wires([0]), par=list()) 86 | 87 | 88 | class Expval(BaseTest): 89 | """test expval() 90 | """ 91 | 92 | def test_expval_exception_if_no_obs_queue(self): 93 | 94 | if self.args.device == 'ibm' or self.args.device == 'all': 95 | dev = ProjectQIBMBackend(wires=2, shots=1, use_hardware=False, token=token, verbose=True) 96 | else: 97 | return 98 | 99 | del dev.__dict__['_obs_queue'] 100 | dev._eng = MagicMock() 101 | dev._eng.backend = MagicMock() 102 | dev._eng.backend.get_probabilities = MagicMock() 103 | dev._eng.backend.get_probabilities.return_value = {'00': 1.0} 104 | 105 | self.assertRaises(DeviceError, dev.expval, 'PauliX', wires=Wires([0]), par=list()) 106 | self.assertRaises(DeviceError, dev.expval, 'PauliY', wires=Wires([0]), par=list()) 107 | self.assertRaises(DeviceError, dev.expval, 'Hadamard', wires=Wires([0]), par=list()) 108 | 109 | if __name__ == '__main__': 110 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', device expval and pre_measure.') 111 | # run the tests in this file 112 | suite = unittest.TestSuite() 113 | for t in (ExpvalAndPreExpvalMock, Expval): 114 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 115 | suite.addTests(ttt) 116 | 117 | unittest.TextTestRunner().run(suite) 118 | -------------------------------------------------------------------------------- /tests/test_projectq_import.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` devices' behavior when applying unsupported operations. 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | import sys 21 | from unittest import mock 22 | from pkg_resources import iter_entry_points 23 | from defaults import pennylane as qml, BaseTest 24 | 25 | log.getLogger('defaults') 26 | 27 | 28 | class ProjectQImportTest(BaseTest): 29 | """test of projectq import. 30 | """ 31 | 32 | def test_projectq_import(self): 33 | """Check that from projectq.ops import MatrixGate can raise an exception without problems, this ensures backward compatibility with older versions of ProjectQ 34 | """ 35 | del sys.modules["pennylane_pq.pqops"] 36 | import projectq.ops 37 | if 'MatrixGate' in projectq.ops.__dict__: 38 | del projectq.ops.__dict__['MatrixGate'] 39 | import pennylane_pq.pqops 40 | 41 | del sys.modules["pennylane_pq.pqops"] 42 | import projectq.ops 43 | if 'MatrixGate' not in projectq.ops.__dict__: 44 | projectq.ops.__dict__['MatrixGate'] = projectq.ops.__dict__['BasicGate'] 45 | import pennylane_pq.pqops 46 | 47 | # restore 48 | del sys.modules["projectq.ops"] 49 | import pennylane_pq.pqops 50 | 51 | if __name__ == '__main__': 52 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', import test.') 53 | # run the tests in this file 54 | suite = unittest.TestSuite() 55 | for t in (ProjectQImportTest, ): 56 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 57 | suite.addTests(ttt) 58 | 59 | unittest.TextTestRunner().run(suite) 60 | -------------------------------------------------------------------------------- /tests/test_unsupported_operations.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for the :mod:`pennylane_pq` devices' behavior when applying unsupported operations. 16 | """ 17 | 18 | import unittest 19 | import logging as log 20 | from defaults import pennylane as qml, BaseTest 21 | import pennylane 22 | from pennylane_pq.devices import ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend 23 | 24 | log.getLogger('defaults') 25 | 26 | 27 | class UnsupportedOperationTest(BaseTest): 28 | """test that unsupported operations/expectations raise DeviceErrors. 29 | """ 30 | 31 | num_subsystems = 4 32 | devices = None 33 | 34 | def setUp(self): 35 | super().setUp() 36 | 37 | self.devices = [] 38 | if self.args.device == 'simulator' or self.args.device == 'all': 39 | self.devices.append(ProjectQSimulator(wires=self.num_subsystems, verbose=True)) 40 | if self.args.device == 'ibm' or self.args.device == 'all': 41 | ibm_options = pennylane.default_config['projectq.ibm'] 42 | if "token" in ibm_options: 43 | self.devices.append(ProjectQIBMBackend(wires=self.num_subsystems, use_hardware=False, num_runs=8 * 1024, 44 | token=ibm_options['token'], verbose=True)) 45 | else: 46 | log.warning("Skipping test of the ProjectQIBMBackend device because IBM login credentials " 47 | "could not be found in the PennyLane configuration file.") 48 | if self.args.device == 'classical' or self.args.device == 'all': 49 | self.devices.append(ProjectQClassicalSimulator(wires=self.num_subsystems, verbose=True)) 50 | 51 | def test_unsupported_operation(self): 52 | if self.devices is None: 53 | return 54 | self.logTestName() 55 | 56 | class SomeOperation(qml.operation.Operation): 57 | num_params = 0 58 | num_wires = 1 59 | par_domain = 'A' 60 | 61 | for device in self.devices: 62 | @qml.qnode(device) 63 | def circuit(): 64 | SomeOperation(wires=0) 65 | return qml.expval(qml.PauliZ(0)) 66 | 67 | self.assertRaises(pennylane._device.DeviceError, circuit) 68 | 69 | def test_unsupported_expectation(self): 70 | if self.devices is None: 71 | return 72 | self.logTestName() 73 | 74 | class SomeObservable(qml.operation.Observable): 75 | num_params = 0 76 | num_wires = 1 77 | par_domain = 'A' 78 | 79 | for device in self.devices: 80 | @qml.qnode(device) 81 | def circuit(): 82 | return qml.expval(SomeObservable(wires=0)) #this expectation will never be supported 83 | 84 | self.assertRaises(pennylane._device.DeviceError, circuit) 85 | 86 | 87 | if __name__ == '__main__': 88 | print('Testing PennyLane ProjectQ Plugin version ' + qml.version() + ', unsupported operations.') 89 | # run the tests in this file 90 | suite = unittest.TestSuite() 91 | for t in (UnsupportedOperationTest, ): 92 | ttt = unittest.TestLoader().loadTestsFromTestCase(t) 93 | suite.addTests(ttt) 94 | 95 | unittest.TextTestRunner().run(suite) 96 | -------------------------------------------------------------------------------- /tests/test_var.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Xanadu Quantum Technologies Inc. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Unit tests for variance 16 | """ 17 | from unittest.mock import MagicMock 18 | import pytest 19 | 20 | import numpy as np 21 | import pennylane 22 | from pennylane.wires import Wires 23 | 24 | from defaults import TOLERANCE 25 | from pennylane_pq.devices import ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend 26 | import os 27 | 28 | token = os.getenv("IBMQX_TOKEN") 29 | 30 | 31 | @pytest.fixture 32 | def tol(): 33 | """Numerical tolerance""" 34 | return TOLERANCE 35 | 36 | 37 | @pytest.fixture 38 | def dev(DevClass, monkeypatch): 39 | """devices""" 40 | if issubclass(DevClass, ProjectQSimulator): 41 | yield DevClass(wires=1, shots=20000000, verbose=True) 42 | 43 | elif issubclass(DevClass, ProjectQClassicalSimulator): 44 | yield DevClass(wires=1, verbose=True) 45 | 46 | elif issubclass(DevClass, ProjectQIBMBackend): 47 | ibm_options = pennylane.default_config["projectq.ibm"] 48 | init_device = DevClass( 49 | wires=1, 50 | use_hardware=False, 51 | num_runs=8 * 1024, 52 | token=token, 53 | verbose=True, 54 | ) 55 | 56 | with monkeypatch.context() as m: 57 | m.setattr( 58 | "pennylane_pq.devices.ProjectQIBMBackend.obs_queue", 59 | [pennylane.PauliZ(wires=0)], 60 | ) 61 | init_device._eng = MagicMock() 62 | init_device._eng.backend = MagicMock() 63 | init_device._eng.backend.get_probabilities = MagicMock() 64 | yield init_device 65 | 66 | 67 | @pytest.mark.parametrize( 68 | "DevClass", [ProjectQSimulator, ProjectQClassicalSimulator, ProjectQIBMBackend] 69 | ) 70 | def test_var_pauliz(dev, tol): 71 | """Test that variance of PauliZ is the same as I-^2""" 72 | dev.apply("PauliX", wires=Wires([0]), par=[]) 73 | 74 | if isinstance(dev, ProjectQIBMBackend): 75 | dev._eng.backend.get_probabilities.return_value = {"0": 0, "1": 1} 76 | 77 | dev.pre_measure() 78 | var = dev.var("PauliZ", wires=Wires([0]), par=[]) 79 | mean = dev.expval("PauliZ", wires=Wires([0]), par=[]) 80 | dev.post_measure() 81 | 82 | assert np.allclose(var, 1 - mean ** 2, atol=tol, rtol=0) 83 | 84 | 85 | @pytest.mark.parametrize("DevClass", [ProjectQSimulator, ProjectQIBMBackend]) 86 | def test_var_pauliz_rotated_state(dev, tol): 87 | """test correct variance for of a rotated state""" 88 | phi = 0.543 89 | theta = 0.6543 90 | 91 | dev.apply("RX", wires=Wires([0]), par=[phi]) 92 | dev.apply("RY", wires=Wires([0]), par=[theta]) 93 | 94 | if isinstance(dev, ProjectQIBMBackend): 95 | dev._eng.backend.get_probabilities.return_value = { 96 | "0": 0.5 * (1 + np.cos(theta) * np.cos(phi)), 97 | "1": 0.5 * (1 - np.cos(theta) * np.cos(phi)), 98 | } 99 | 100 | dev.pre_measure() 101 | var = dev.var("PauliZ", wires=Wires([0]), par=[]) 102 | dev.post_measure() 103 | expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta) ** 2 * np.cos(2 * phi)) 104 | 105 | assert np.allclose(var, expected, atol=tol, rtol=0) 106 | --------------------------------------------------------------------------------