diff --git a/.coveragerc b/.coveragerc index 915f2a6890..377dc646e2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,8 @@ [run] omit = */tests/* + mitiq/_about.py + [report] # Regexes for lines to exclude from consideration diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e8ddafecc1..32c8d1d56a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -42,6 +42,8 @@ updates: update-types: ["version-update:semver-minor", "version-update:semver-patch"] - dependency-name: "pandas" update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "qrack" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] # Allow up to 10 open pull requests for pip dependencies open-pull-requests-limit: 10 labels: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 587122f822..56af995577 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,11 +22,11 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Install Python dependencies run: | python -m pip install --upgrade pip - make install requirements + make install - name: Check types with mypy run: make check-types @@ -34,52 +34,6 @@ jobs: - name: Check code style/formatting run: make check-format - docs: - runs-on: ubuntu-latest - steps: - - name: Check out mitiq - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - make install requirements - - - name: Run the quilc & qvm Docker images - run: | - docker run --rm -idt -p 5000:5000 rigetti/qvm -S - docker run --rm -idt -p 5555:5555 rigetti/quilc -R - - - name: Generate hash from all files excluding example notebooks - id: gen_hash - run: | - HASH=$(find . -type f -not -path './docs/source/examples/*' | sort | xargs cat | sha256sum | awk '{print $1}') - echo "hash=$HASH" >> $GITHUB_OUTPUT - - - name: Fetch Jupyter cache - uses: actions/cache@v4 - with: - path: ./docs/build/.jupyter_cache - key: jupyter-cache-${{ steps.gen_hash.outputs.hash }} - - - name: Build and test Sphinx docs - run: | - export BQSKIT_DOC_CHECK_OVERRIDE=1 - export PYDEVD_DISABLE_FILE_VALIDATION=1 - make docs - - - name: Run linkcheck on 'release' PRs - if: github.event_name == 'pull_request' - run: | - pr_title_lower=$(echo "${{ github.event.pull_request.title }}" | tr '[:upper:]' '[:lower:]') - if [[ "$pr_title_lower" == *"release"* ]]; then - make linkcheck - fi # This is to make sure Mitiq works without optional 3rd party packages like Qiskit, pyQuil, etc. # E.g., if we accidentally `import qiskit` in Mitiq where we shouldn't, this test will catch that. @@ -91,7 +45,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Install Mitiq # Since requirements.txt includes cirq, which in turn has pyquil as dependency, # we explicitly remove pyquil from the installed packages after installing mitiq @@ -110,7 +64,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] runs-on: ${{ matrix.os }} steps: - name: Check out mitiq @@ -125,7 +79,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - make install requirements + make install - name: Run the quilc & qvm Docker images if: ${{ matrix.os == 'ubuntu-latest' }} run: | @@ -135,7 +89,7 @@ jobs: run: ${{ matrix.os == 'ubuntu-latest' && 'make test-all' || 'make test' }} - name: Submit coverage report to Codecov # Only submit to Codecov once. - if: ${{ matrix.python-version == '3.11' && matrix.os == 'ubuntu-latest'}} + if: ${{ matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'}} uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml new file mode 100644 index 0000000000..544f385e56 --- /dev/null +++ b/.github/workflows/docs-build.yml @@ -0,0 +1,64 @@ +name: docs-build + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: + - main + push: + branches: + - main + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Check out mitiq + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + make install + + - name: Run the quilc & qvm Docker images + run: | + docker run --rm -idt -p 5000:5000 rigetti/qvm -S + docker run --rm -idt -p 5555:5555 rigetti/quilc -R + + - name: Generate hash from all files excluding example notebooks + id: gen_hash + run: | + HASH=$(find . -type f -not -path './docs/source/examples/*' | sort | xargs cat | sha256sum | awk '{print $1}') + echo "hash=$HASH" >> $GITHUB_OUTPUT + + - name: Fetch Jupyter cache + uses: actions/cache@v4 + with: + path: ./docs/build/.jupyter_cache + key: jupyter-cache-${{ steps.gen_hash.outputs.hash }} + + - name: Build and test Sphinx docs + run: | + sudo apt update + sudo apt install ocl-icd-opencl-dev + export BQSKIT_DOC_CHECK_OVERRIDE=1 + export PYDEVD_DISABLE_FILE_VALIDATION=1 + make docs + + - name: Run linkcheck on 'release' PRs + if: github.event_name == 'pull_request' + run: | + pr_title_lower=$(echo "${{ github.event.pull_request.title }}" | tr '[:upper:]' '[:lower:]') + if [[ "$pr_title_lower" == *"release"* ]]; then + make linkcheck + fi diff --git a/.github/workflows/greeting.yml b/.github/workflows/greeting.yml index e0c5a11d08..3a2e0faa2a 100644 --- a/.github/workflows/greeting.yml +++ b/.github/workflows/greeting.yml @@ -10,6 +10,3 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: 'Hello @${{ github.actor }}, thank you for submitting a PR to Mitiq! We will respond as soon as possible, and if you have any questions in the meantime, you can ask us on the [Unitary Fund Discord](http://discord.unitary.fund).' - issue-message: | - Hello @${{ github.actor }}, thank you for your interest in Mitiq! - If this is a bug report, please provide screenshots and/or **minimum viable code to reproduce your issue**, so we can do our best to help get it fixed. If you have any questions in the meantime, you can also ask us on the [Unitary Fund Discord](http://discord.unitary.fund). diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 77fe5f9423..d426277562 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -21,11 +21,11 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Install Python dependencies run: | python -m pip install --upgrade pip - make install requirements + make install pip install setuptools wheel twine - name: Build and publish env: diff --git a/.github/workflows/publish-testpypi.yml b/.github/workflows/publish-testpypi.yml index 76e5c51629..5e5823428b 100644 --- a/.github/workflows/publish-testpypi.yml +++ b/.github/workflows/publish-testpypi.yml @@ -8,7 +8,8 @@ on: - cron: "0 0 * * *" jobs: - deploy: + build: + name: Build Mitiq if: github.repository_owner == 'unitaryfund' runs-on: ubuntu-latest steps: @@ -17,33 +18,39 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" - name: Install Python dependencies run: | python -m pip install --upgrade pip - make install requirements - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.TESTPYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.TESTPYPI_PASSWORD }} + pip install build + - name: change mitiq version run: | - python setup.py sdist bdist_wheel - twine upload --repository testpypi dist/* + truncate -s -1 VERSION.txt + echo "$(date +"%Y%m%d")" >> VERSION.txt + - name: Build mitiq + run: python -m build + - name: Store build artifacts + uses: actions/upload-artifact@v4 + with: + name: build + path: dist/* - # TODO: Need a way to get the version number from previous step - # download-test: - # runs-on: ubuntu-latest - # steps: - # - name: Check out mitiq - # uses: actions/checkout@v4 - # - name: Set up Python - # uses: actions/setup-python@v5 - # with: - # python-version: 3.8 - # - name: Install Mitiq - # The ``--extra-index-url`` is necessary since otherwise ``TestPyPI`` would be - # looking for the required dependencies therein, but we want it to install them - # from the real PyPI channel. - # run: | - # pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.python.org/simple/ mitiq==x.y.z + test-publish: + name: Upload release to test PyPi + runs-on: ubuntu-latest + needs: build + environment: + name: testpypi + url: https://test.pypi.org/p/mitiq/ + permissions: + id-token: write + steps: + - name: Fetch build artifacts + uses: actions/download-artifact@v4 + with: + name: build + path: dist/ + - name: Publish package distributions to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/.gitignore b/.gitignore index caaefa6147..1770047cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .vscode .python-version .idea/ -.coverage *.dat *.ipynb *.hdf5 @@ -11,8 +10,13 @@ _build __pycache__/ docs/build/ +docs/source/tags/ mitiq.egg-info/ dist/ build/ -coverage.xml jupyter_execute/ +.mypy_cache/ +# Coverage reports +coverage.xml +.coverage +.coverage.* diff --git a/.readthedocs.yml b/.readthedocs.yml index f91e870862..8c28901563 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -9,7 +9,9 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" + apt_packages: + - ocl-icd-opencl-dev # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/AUTHORS b/AUTHORS index e018e92d74..d253a68a5b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,3 +58,4 @@ Yash Prabhat Vladimir Kozhukalov Francesc Sabater Emiliano Godinez +Tommy Nguyen diff --git a/CHANGELOG.md b/CHANGELOG.md index b65389785d..07310f3d7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,127 @@ # Changelog -## Version 0.38.0 -_In development_ +## Version 0.41.0 + +_In development._ + +## Version 0.40.0 + +([Full Changelog](https://github.com/unitaryfund/mitiq/compare/v0.39.0...v0.40.0)) + +### Highlights + +🔉 A new quantum error-mitigation technique is available in Mitiq! +**Layerwise Richardson Extrapolation** is now available through `mitiq.lre.execute_with_lre`. +Documentation is also available in the user guide, with more advanced docs and demonstrations coming in the next release. +Special thanks to Purva Thakre for this contribution! + +🥇 We had two **first time contributions** from @ecarlander and @mbrotos! +Thank you both for your contributions! + +🛡️ A **helpful error message** is raised when passing data of the incorrect type to the `MeasurementResult` class, where before it silently gave confusing results. + +#### ✨ Enhancements +- LRE Executors (#2499) [@purva-thakre] +- LRE Inference Functions (#2447) [@purva-thakre] +- Raise TypeError when a dictionary is passed to MeasurementResult constructor (#2523) [@natestemen] + +#### 📓 Documentation + +- Add theory, intro and use case pages of LRE user guide (#2522) [@purva-thakre] +- add QOSS survey banner (#2533) [@natestemen] +- Update broken link in the readme (#2528) [@purva-thakre] +- Move class documentation to class docstrings (#2525) [@natestemen] +- QSE docs cleanup (#2490) [@natestemen] +- Add tags to tutorials (#2467) [@purva-thakre] +- Correct CDR and VNCDR acronyms in example (#2479) [@bdg221] +- added roadmap link to readme (#2468) [@ecarlander] +- Update ibmq-backends.md (#2474) [@mbrotos] + +#### 🧑🏽‍💻 Developer Improvements + +- Remove `make requirements` (#2481) [@purva-thakre] +- Fix flaky REM test / refactor (#2464) [@natestemen] + +#### 📦 Dependency Updates + +- Bump pyscf from 2.6.2 to 2.7.0 (#2518) [@dependabot] +- Bump pyqrack from 1.30.24 to 1.30.30 (#2521) [@dependabot] +- Bump pyqrack from 1.30.22 to 1.30.24 (#2497) [@dependabot] +- Update qiskit requirement from ~=1.2.1 to ~=1.2.2 (#2507) [@dependabot] +- Bump qibo from 0.2.10 to 0.2.12 (#2506) [@dependabot] +- Update qiskit-aer requirement from ~=0.15.0 to ~=0.15.1 (#2504) [@dependabot] +- Update qiskit requirement from ~=1.2.0 to ~=1.2.1 (#2503) [@dependabot] +- Bump pyqrack from 1.30.20 to 1.30.22 (#2489) [@dependabot] +- Update qiskit-aer requirement from ~=0.14.2 to ~=0.15.0 (#2484) [@dependabot] +- Bump pyqrack from 1.30.8 to 1.30.20 (#2487) [@dependabot] +- Update cirq-core requirement from <1.4.0,>=1.0.0 to >=1.0.0,<1.5.0 (#2390) [@dependabot] +- Update qiskit requirement from ~=1.1.1 to ~=1.2.0 (#2482) [@dependabot] +- Update scipy requirement from <=1.14.0,>=1.10.1 to >=1.10.1,<=1.14.1 (#2477) [@dependabot] +- Bump pyqrack from 1.30.0 to 1.30.8 (#2476) [@dependabot] +- Bump sphinx from 7.2.6 to 8.0.2 (#2455) [@dependabot] +- Bump qibo from 0.2.9 to 0.2.10 (#2458) [@dependabot] + +## Version 0.39.0 + +([Full Changelog](https://github.com/unitaryfund/mitiq/compare/v0.38.0...v0.39.0)) + +### Highlights + +We've made updates to our documentation, beginning with the completion of the first section of the Pauli Twirling user guide, which offers a comprehensive introduction to this feature. +Additionally, we've added a new tutorial on CDR (Clifford Data Regression) using [Qrack](https://github.com/unitaryfund/qrack/) as an efficient near-Clifford simulator. +This demonstrates a workflow that harnesses the speed of Qrack in the CDR training phase, while providing users with an in-depth look at how to integrate Mitiq and Qrack effectively. + +#### 📓 Documentation + +- Complete first section of Pauli Twirling user guide (#2454) [@cosenal] +- Hide primary sidebar from certain pages of the documentation (#2424) [@purva-thakre] +- CDR Tutorial with Qrack (#2451) [@bdg221] + +#### 🧑🏽‍💻 Developer Improvements + +- Separate docs build workflow (#2441) [@purva-thakre] +- clean and git ignore all coverage reports (#2443) [@cosenal] +- Ignore _about.py in pytest coverage (#2379) [@purva-thakre] +- use date to make version unique for testpypi uploads (#2436) [@natestemen] + +#### 📦 Dependency Updates + +- Update scipy requirement from <=1.13.1,>=1.10.1 to >=1.10.1,<=1.14.0 (#2420) [@dependabot] + +## Version 0.38.0 + +([Full Changelog](https://github.com/unitaryfund/mitiq/compare/v0.37.0...v0.38.0)) + +### Highlights + +🚀 As of this release, thanks to @natestemen, **we are officially supporting Python 3.12 and dropping Python 3.9**. + +🌉 As part of UnitaryHack 2024, new contributor @NnguyenHTommy **fixed a Qiskit to Cirq gate conversion error** by implementing a fallback mechanism to decompose and transpile the Qiskit circuit into native Cirq gates. + +🌀 Another Unitary Hacker, @EmilianoG-byte **added functionality to simulate noise specifically on CNOT and CZ gates when using the Pauli Twilring technique** to symmetrize errors. + +🔉 As hinted in last release's spoilers, @purva-thakre has **implemented the noise scaling functionality required for the Layerwise Richardson Extrapolation (LRE) technique**, which allows a more fine-grained control over the amount of noise in circuits compared to the standard unitary folding method. + +### ✨ Enhancements +- **Noise Scaling for LRE** (#2347) [@purva-thakre] +- **Issue #2354 Fix - qiskit QFT gates error during conversion** (#2404) [@NnguyenHTommy] +- **Simulate noise for CNOT and CZ gates in Pauli Twirling** (#2397) [@EmilianoG-byte] + +### 🛠️ Maintenance and Upkeep Improvements +- Update readme (#2421) [@purva-thakre] +- Update README with Github discussion link (#2419) [@bdg221] +- Update contributing guide (#2382) [@purva-thakre] + +### 🧑🏽‍💻 Dev Environment Improvements +- Used trusted publishers for testpypi publishing (#2320) [@natestemen] +- bump min python version for intersphinx map (#2425) [@natestemen] +- **Support python 3.12 and drop 3.9** (#2066) [@natestemen] + +#### 📦 Dependency Updates +- Bump qibo from 0.2.8 to 0.2.9 (#2430) [@dependabot[bot]] +- Update qiskit requirement from ~=1.1.0 to ~=1.1.1 (#2414) [@dependabot[bot]] +- Bump pyscf from 2.6.0 to 2.6.2 (#2415) [@dependabot[bot]] + ## Version 0.37.0 @@ -67,7 +187,7 @@ mitiq.qem_methods() ``` This function provides an accessible way to understand the module naming of each technique supported by Mitiq. -Thanks to @andre-a-alves, @cosenal, @jordandsulliva, @mistywahl, @purva-thakre for the PRs in this milestone. +Thanks to @andre-a-alves, @cosenal, @jordandsullivan, @mistywahl, @purva-thakre for the PRs in this milestone. ### Enhancements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b73001c5f7..92ed250bcd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,14 +5,14 @@ The most common ways to contribute here are 1. opening an [issue](https://github.com/unitaryfund/mitiq/issues/new) to report a bug or propose a new feature, or ask a question, and 2. opening a [pull request](https://github.com/unitaryfund/mitiq/pulls) to fix a bug, or implement a desired feature. -3. opening a [discussion post](https://github.com/unitaryfund/mitiq/discussions) to ask a question (no stupid questions!), provide feedback, or show something off! +3. opening a [discussion post](https://github.com/unitaryfund/mitiq/discussions) to ask a question (all questions welcome!), provide feedback, or show something off! The rest of this document describes the technical details of getting set up to develop, and make your first contribution to Mitiq. ## Development environment -1. Ensure you have python 3.9 or greater installed. If not, you can find the downloads [here](https://www.python.org/downloads/). +1. Ensure you have python 3.10 or greater installed. If not, you can find the downloads [here](https://www.python.org/downloads/). 2. Set up a virtual environment to isolate dependencies. This can be done with many different tools including [Virtualenv](https://virtualenv.pypa.io/en/latest/), [Pipenv](https://pypi.org/project/pipenv/), [Poetry](https://python-poetry.org/), and [Anaconda](https://www.anaconda.com/download). In what follows we will use Anaconda, but if you're familiar with other tools feel free to use those. 3. Set up a local version of the [Mitiq repository](https://github.com/unitaryfund/mitiq). To do this you will need to use `git` which is a version control system. If you're unfamiliar, check out the [docs](https://git-scm.com/), and learn about what the typical [`git` workflow](https://www.asmeurer.com/git-workflow/) looks like. 4. Inside the Mitiq repository (`cd mitiq`), activate a virtual environment. With conda this is done using the following command. @@ -47,7 +47,7 @@ Once they pass, you can run the entire test suite (excluding those that require make test ``` -This can often be slow, however, so testing your changes iteratively using `pytest` is often faster when doing development. +This can often be slow, however, so testing your changes [iteratively](https://docs.pytest.org/en/7.1.x/how-to/usage.html#specifying-which-tests-to-run) using `pytest` is often faster when doing development. To run the tests for the pyQuil plugins, run ```bash @@ -68,7 +68,7 @@ docker run --rm -idt -p 5555:5555 rigetti/quilc -R ``` ### Updating the documentation -Follow these [instructions for contributing to the documentation](https://mitiq.readthedocs.io/en/latest/contributing_docs.html) which include guidelines about updating the API-doc list of modules and writing examples in the users guide. +Follow these [instructions for contributing to the documentation](contributing_docs.md) which include guidelines about updating the API-doc, adding examples, and updating the user guide. ### Style guidelines @@ -116,14 +116,26 @@ This is a list of accepted request-for-comments (RFC) documents by date of creat - [Error Mitigation by Subspace Expansion](https://docs.google.com/document/d/1JyQAwiw8BRT_oucZ6tQv0id6UhSdd3df1mNSPpOvu1I) by Ammar Jahin, Dariel Mok , Preksha Naik, Abdulrahman Sahmoud (@bubakazouba) Apr 28, 2023 - [Implementation RFC for Mitiq calibration](https://docs.google.com/document/d/1EZUJyEEUQUH33UOgSIzCCvXyxP0WLOQn11W0x4Ox4nY/edit) by Andrea Mari (@andreamari) Nov 2, 2022 - [Calibration tools for error mitigation RFC (abstract general solutions)](https://docs.google.com/document/d/1otUHnTlyNS-0rxGAxltHLF1iD5C9qT9oEZ3jn8VHWgw/edit) by Andrea Mari (@andreamari) Oct 6, 2022 -- [Identity insersion scaling RFC](https://docs.google.com/document/d/1hbd9frjYiSy0WujA0iCccc-oMO4Q-kZc2G4b3lkJHdk/edit) by Purva Thakre (@purva-thakre) Jun 29, 2022 +- [Identity insertion scaling RFC](https://docs.google.com/document/d/1hbd9frjYiSy0WujA0iCccc-oMO4Q-kZc2G4b3lkJHdk/edit) by Purva Thakre (@purva-thakre) Jun 29, 2022 - [Readout Confusion Inversion RFC](https://docs.google.com/document/d/1buO5PrO5sS02VXjcaYf37RuR0rF6xpyr4J9H1tI4vN4/edit) by Amir Ebrahimi (@amirebrahimi) Jun 16, 2022 - [Documentation reorganization RFC](https://docs.google.com/document/d/13un5TZPknSOhmOBkrL2rsofjGfdp2jDnd-DywLpGFPc/edit) by Ryan LaRose (@rmlarose) Dec 1, 2021 - [Learning-based PEC RFC](https://docs.google.com/document/d/1VItesy6R5SlUa_YXW1km7IjFZ8kzyFeHUepHak1fEh4/edit) by Misty Wahl (@Misty-W) Oct 25, 2021 - [Digital dynamical decoupling RFC](https://docs.google.com/document/d/1cRwFCTn6kUjI1P0kNydtevxIYtE4r8Omd_iWK0Pe8qo/edit) by Aaron Robertson (@Aaron-Robertson) Jan 28, 2021 +### Checklist for adding an approved QEM Technique + +After your RFC is accepted, the proposed feature (for example, a new QEM Method) will require the following: + +- Add the new QEM method to `mitiq/abbreviated_name_of_qem_method` such that the corresponding units tests are in `mitiq/abbreviated_name_of_qem_method/tests` +- The code must follow the formatting and style guidelines discussed [above](#style-guidelines), +- The new module should be added to the [](apidoc.md) using the instructions found in [](contributing_docs.md#automatically-add-information-from-the-api-docs), +- Add documentation for the new QEM method, additional details are available in [](contributing_docs.md#adding-files-to-the-user-guide), +- Update `docs/source/guide/glossary.md` with a one-line summary of what your new feature accomplishes, and +- Update the [](./readme.md#quick-tour) section of the `README.md` with information related to your new technique. + + ## Code of conduct -Mitiq development abides to the [Contributors' Covenant](https://mitiq.readthedocs.io/en/latest/code_of_conduct.html). +Mitiq development abides to the [](./code_of_conduct.md). ## Lifecycle The basic development workflow for Mitiq is done in units of milestones which are usually one month periods where we focus our efforts on thrusts decided by the development team, alongside community members. @@ -132,5 +144,3 @@ Milestones are tracked using the [GitHub milestone feature](https://github.com/u All releases for Mitiq are tagged on the `main` branch with tags for the version number of the release. Find all the previous releases [here](https://github.com/unitaryfund/mitiq/releases). -## Code of conduct -Mitiq development abides to the [Contributors' Covenant](https://mitiq.readthedocs.io/en/latest/code_of_conduct.html). diff --git a/Makefile b/Makefile index 39c30f495b..e85de221bc 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,9 @@ check-types: clean: rm -rf dist rm -rf mitiq.egg-info - rm -rf .pytest_cache/ + rm -rf .mypy_cache .pytest_cache .ruff_cache + rm -rf htmlcov coverage.xml .coverage .coverage.* + rm -rf .ipynb_checkpoints .PHONY: dist dist: @@ -54,10 +56,6 @@ install-hooks: @chmod +x .git-hooks/* @echo "Git hooks installed." -.PHONY: requirements -requirements: requirements/requirements.txt - pip install -r requirements/requirements.txt - .PHONY: test test: pytest -n auto -v --cov=mitiq --cov-report=term --cov-report=xml --ignore=mitiq/interface/mitiq_pyquil diff --git a/README.md b/README.md index 80f77c8f5d..6dee8d2f4e 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,13 @@ imperfect gate applications, state preparation and measurement errors, etc. Error mitigation seeks to reduce these effects at the software level by compiling quantum programs in clever ways. -Want to know more? Check out our -[documentation](https://mitiq.readthedocs.io/en/stable/guide/guide.html), chat with us on [Discord](http://discord.unitary.fund), and join our weekly community call ([public agenda](https://docs.google.com/document/d/1lZfct4AOCS7fdyWkudcGyER0n0nsCxSFKSicUEeJgtA/)). +Want to know more? +- Check out our +[documentation](https://mitiq.readthedocs.io/en/stable/guide/guide.html). +- To see what's in store for Mitiq, look at our roadmap in the [wiki](https://github.com/unitaryfund/mitiq/wiki). +- For code, repo, or theory questions, especially those requiring more detailed responses, submit a [Discussion](https://github.com/unitaryfund/mitiq/discussions). +- For casual or time sensitive questions, chat with us on [Discord](http://discord.unitary.fund). +- Join our weekly community call on [Discord](http://discord.unitary.fund) ([public agenda](https://docs.google.com/document/d/1lZfct4AOCS7fdyWkudcGyER0n0nsCxSFKSicUEeJgtA/)). ## Quickstart @@ -90,7 +95,22 @@ mitiq.qem_methods() | Probabilistic error cancellation | [PEC](https://mitiq.readthedocs.io/en/latest/guide/pec.html) | [`mitiq.pec`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/pec) | [1612.02058](https://arxiv.org/abs/1612.02058)
[1712.09271](https://arxiv.org/abs/1712.09271)
[1905.10135](https://arxiv.org/abs/1905.10135) | | (Variable-noise) Clifford data regression | [CDR](https://mitiq.readthedocs.io/en/latest/guide/cdr.html) | [`mitiq.cdr`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/cdr) | [2005.10189](https://arxiv.org/abs/2005.10189)
[2011.01157](https://arxiv.org/abs/2011.01157) | | Digital dynamical decoupling | [DDD](https://mitiq.readthedocs.io/en/latest/guide/ddd.html) | [`mitiq.ddd`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/ddd) | [9803057](https://arxiv.org/abs/quant-ph/9803057)
[1807.08768](https://arxiv.org/abs/1807.08768) | -| Readout-error mitigation | [REM](https://mitiq.readthedocs.io/en/latest/guide/rem.html) | [`mitiq.rem`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/rem) | [1907.08518](https://arxiv.org/abs/1907.08518)
[2006.14044](https://arxiv.org/abs/2006.14044) | +| Readout-error mitigation | [REM](https://mitiq.readthedocs.io/en/latest/guide/rem.html) | [`mitiq.rem`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/rem) | [1907.08518](https://arxiv.org/abs/1907.08518)
[2006.14044](https://arxiv.org/abs/2006.14044) +| Quantum Subspace Expansion | [QSE](https://mitiq.readthedocs.io/en/stable/guide/qse.html) | [`mitiq.qse`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/qse) | [1903.05786](https://arxiv.org/abs/1903.05786)| +| Robust Shadow Estimation 🚧 | [RSE](https://mitiq.readthedocs.io/en/stable/guide/shadows.html)| [`mitiq.qse`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/shadows) | [2011.09636](https://arxiv.org/abs/2011.09636)
[2002.08953](https://arxiv.org/abs/2002.08953)| +| Layerwise Richardson Extrapolation 🚧 | Coming soon | [`mitiq.lre`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/lre) | [2402.04000](https://arxiv.org/abs/2402.04000) | + + +In addition, we also have a noise tailoring technique currently available with limited functionality: + + +| Noise-tailoring Technique | Documentation | Mitiq module | Paper Reference(s) | +| ----------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pauli Twirling 🚧 | [PT](https://mitiq.readthedocs.io/en/latest/guide/pt.html) | [`mitiq.pt`](https://github.com/unitaryfund/mitiq/tree/main/mitiq/pt) | [1512.01098](https://arxiv.org/abs/1512.01098) | + +> 🚧: Technique is currently a work in progress or is untested and may have some rough edges. If you try any of these techniques and have suggestions, please open an issue! + + See our [roadmap](https://github.com/unitaryfund/mitiq/wiki) for additional candidate techniques to implement. If there is a technique you are looking for, please file a [feature request](https://github.com/unitaryfund/mitiq/issues/new?assignees=&labels=feature-request&template=feature_request.md&title=). @@ -103,13 +123,13 @@ We refer to any programming language you can write quantum circuits in as a _fro | [Cirq](https://quantumai.google/cirq) | [Qiskit](https://www.ibm.com/quantum/qiskit) | [pyQuil](https://github.com/rigetti/pyquil) | [Braket](https://github.com/aws/amazon-braket-sdk-python) | [PennyLane](https://pennylane.ai/) | [Qibo](https://qibo.science/) | |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| Cirq logo | Qiskit logo | Rigetti logo | AWS logo | PennyLane logo | Qibo logo | +| Cirq logo | Qiskit logo | Rigetti logo | AWS logo | PennyLane logo | Qibo logo | You can install Mitiq support for these frontends by specifying them during installation, as optional extras, along with the main package. To install Mitiq with one or more frontends, you can specify each frontend in square brackets as part of the installation command. -For example, +For example, to install Mitiq with support for Qiskit and Qibo: ```bash pip install mitiq[qiskit,qibo] @@ -127,7 +147,7 @@ You can use Mitiq with any backend you have access to that can interface with su If you use Mitiq in your research, please reference the [Mitiq whitepaper](https://quantum-journal.org/papers/q-2022-08-11-774/) using the bibtex entry found in [`CITATION.bib`](https://github.com/unitaryfund/mitiq/blob/main/CITATION.bib). -A list of papers citing Mitiq can be found on [Google Scholar](https://scholar.google.com/scholar?cites=12810395086731011605) / [Semantic Scholar](https://api.semanticscholar.org/CorpusID:221555755?). +A list of papers citing Mitiq can be found on [Google Scholar](https://scholar.google.com/scholar?oi=bibs&hl=en&cites=1985661232443186918) / [Semantic Scholar](https://api.semanticscholar.org/CorpusID:221555755?). ## License diff --git a/VERSION.txt b/VERSION.txt index 44eb47d509..2b81016649 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -0.38.0dev +0.41.0dev diff --git a/docs/CONTRIBUTING_DOCS.md b/docs/CONTRIBUTING_DOCS.md index a6d4821364..4d3af7351c 100644 --- a/docs/CONTRIBUTING_DOCS.md +++ b/docs/CONTRIBUTING_DOCS.md @@ -39,16 +39,14 @@ extensions = ['sphinx.ext.autodoc'] Documentation is found in `docs/source`, and is divided into the following sections: -- a **guide**, whose content needs to be -written from scratch, -- **examples** which can be either jupyter notebooks or MyST formatted notebooks, and -- an **API-doc** part, which is (mostly) -automatically generated. +- a **guide**, whose content needs to be written from scratch for new features. For more details, go to [](contributing.md#proposing-a-new-feature-to-mitiq) and [](contributing_docs.md#adding-files-to-the-user-guide) +- **examples** which can be either a jupyter notebook or a MyST formatted notebook. A checklist is available in [](contributing_docs.md#adding-example-files) +- an **API-doc** part, which is (mostly) automatically generated. See [](contributing_docs.md#automatically-add-information-from-the-api-docs) for additional information. Information in the docs should be added as markdown files using the MyST markdown syntax. If you are adding a new file (as opposed to editing an existing one), ensure to add it to an associated TOC so that it is discoverable. -The main table of contents (TOC) file for the docs is `index.md`. It includes `guide\guide.md` and `apidoc.md`, among other files. To add a new file to the base TOC, make sure it gets listed in the `toctree` directive like this: +The main table of contents (TOC) file for the docs is `index.md`. It includes `guide/guide.md` and `apidoc.md`, among other files. To add a new file to the base TOC, make sure it gets listed in the `toctree` directive like this: ```` ```{toctree} --- @@ -76,12 +74,56 @@ To include `.md` files outside of the documentation `source` directory, you can where `file.md` is the one to be added. For more information on including files external to the docs, see the [MyST docs](https://myst-parser.readthedocs.io/en/latest/). + + ### Adding files to the user guide To add information in the guide, please add markdown (`.md`) files to the `docs/guide` directory. Remember to add new files to the guide's TOC file `docs/source/guide/guide.md`. -### Adding workflow images to the user guide +#### User Guide Outline + +The different sections of a User's guide comprises of: + +- How do I use `new_QEM_method` labeled as `new_QEM_method-1-intro.md`? +- When should I use `new_QEM_method` labeled as `new_QEM_method-2-use-case.md`? +- What additional options are available in `new_QEM_method` labeled as `new_QEM_method-3-options.md`? +- What happens when I use `new_QEM_method` labeled as `new_QEM_method-4-low-level.md`? +- What is the theory behind `new_QEM_method` labeled as `new_QEM_method-5-theory.md`? + +The main landing page will link all the sections of the User's Guide such that there is a [workflow diagram](https://mitiq.readthedocs.io/en/latest/contributing_docs.html#adding-workflow-images-to-the-user-guide) for the `new_QEM_method` on this page alongside +links to any tutorials utilizing `new_QEM_method` in `docs/source/examples`. + +```{code-block} + # Full Name of New QEM Method + + One line summary of what the technique does + + {figure} ../img/new_qem_method_workflow.png + --- + width: 700px + name: new_qem_method_workflow_overview + --- + The new_QEM workflow in Mitiq is fully explained in the {doc}`new_QEM_method-4-low-level.md` section. + + + Below you can find sections of the documentation that address the following questions: + + {toctree} + --- + maxdepth: 1 + --- + new_QEM_method-1-intro.md + new_QEM_method-2-use-case.md + new_QEM_method-3-options.md + new_QEM_method-4-low-level.md + new_QEM_method-theory.md + + + A simple tutorial using `new_QEM_method` can be found +``` + +#### Adding workflow images to the user guide To insert a workflow to the user's guide of some new technique, a template is available (shown below as a `png`). This template is also available in `svg` format at [mitiq_template.svg](../source/img/general_template.svg). @@ -108,14 +150,22 @@ Jupyter notebooks (`.ipynb`) or MyST markdown notebooks, but MyST formatting wil If you have a notebook you want to add, and want to automatically convert it from the `.ipynb` to `.md`, you can use a great Python command line tool called [jupytext](https://jupytext.readthedocs.io/en/latest/index.html). To convert from an IPython notebook to markdown file, run `jupytext your_filename.ipynb --to myst` and find the converted file at `your_filename.md`. -Futher, not only can `jupytext` convert between the formats on demand, but once you install it, you can configure it to manage _both_ a Jupyter and Markdown version of your file, so you don't have to remember to do conversions (for more details, see the `jupytext` docs on [paired notebooks](https://jupytext.readthedocs.io/en/latest/index.html#paired-notebooks). +Further, not only can `jupytext` convert between the formats on demand, but once you install it, you can configure it to manage _both_ a Jupyter and Markdown version of your file, so you don't have to remember to do conversions (for more details, see the `jupytext` docs on [paired notebooks](https://jupytext.readthedocs.io/en/latest/index.html#paired-notebooks)). Using the paired notebooks you can continue your development in the notebooks as normal, and just commit to git the markdown serialized version when you want to add to the docs. You can even add this tool as a [git pre-commit hook](https://jupytext.readthedocs.io/en/latest/using-pre-commit.html) if you want! ```{tip} -There is a [sample markdown formatted notebook in the `examples` directory](./examples/template.md) for you to take a look at as you write your own! +There is a [sample markdown formatted notebook in the `examples` directory](../source/examples/template.md) for you to take a look at as you write your own! ``` +### Adding example files + +If you are adding a new tutorial to the [documentation examples](../source/examples/examples.md), + +- add the file as a `.md` file in `docs/source/examples`. A template is available at [here](../source/examples/template.md). +- add your tutorial in `docs/source/examples/examples.md` such that it shows up in a docs build +- add a thumbnail image as `docs/source/_thumbnails/your_image.png` and list it in the `nbsphinx_thumbnails` block of `docs/source/conf.py` + ### Automatically add information from the API docs New modules, classes and functions can be added by listing them in the appropriate file (such as `apidoc.md` or a child), e.g., @@ -142,7 +192,7 @@ source code, and to the API page `apidoc.md`. ### Adding references -To add references to the [Mitiq bibliography](https://mitiq.readthedocs.io/en/stable/bibliography.html), the first step is to add the reference to `docs/source/refs.bib` which is organized alphabetically. For formatting, please see BibTeX documentation for [articles](https://www.bibtex.com/e/article-entry/), [books](https://www.bibtex.com/e/book-entry/), and [others](https://www.bibtex.com/e/entry-types/). +To add references to the [Mitiq bibliography](bibliography.md), the first step is to add the reference to `docs/source/refs.bib` which is organized alphabetically. For formatting, please see BibTeX documentation for [articles](https://www.bibtex.com/e/article-entry/), [books](https://www.bibtex.com/e/book-entry/), and [others](https://www.bibtex.com/e/entry-types/). Once the reference has been added to the `docs/source/refs.bib` file, cite the reference in the file by using: ```md @@ -162,7 +212,7 @@ To call sphinx directly, `cd` into the `docs` directory and run sphinx-build -b html source build ``` -These commands generate the `docs/build` folder, which is ignore by the `.gitignore` file. +These commands generate the `docs/build` folder, which is ignored by the `.gitignore` file. Once the documentation is generated you can view it by opening it in your browser. ## Testing the Documentation diff --git a/docs/source/_thumbnails/cdr-qrack.png b/docs/source/_thumbnails/cdr-qrack.png new file mode 100644 index 0000000000..501a1ef4f9 Binary files /dev/null and b/docs/source/_thumbnails/cdr-qrack.png differ diff --git a/docs/source/apidoc.md b/docs/source/apidoc.md index f0a572f2dc..b489a3f6d9 100644 --- a/docs/source/apidoc.md +++ b/docs/source/apidoc.md @@ -87,6 +87,23 @@ See Ref. {cite}`Czarnik_2021_Quantum` for more details on these methods. :members: ``` +### Layerwise Richardson Extrapolation + +```{eval-rst} +.. automodule:: mitiq.lre.lre + :members: +``` + +```{eval-rst} +.. automodule:: mitiq.lre.multivariate_scaling.layerwise_folding + :members: +``` + +```{eval-rst} +.. automodule:: mitiq.lre.inference.multivariate_richardson + :members: +``` + ### Pauli Twirling ```{eval-rst} @@ -365,6 +382,7 @@ See Ref. {cite}`Czarnik_2021_Quantum` for more details on these methods. .. automodule:: mitiq.interface.mitiq_pyquil.conversions :members: ``` + #### Qibo Conversions ```{eval-rst} diff --git a/docs/source/conf.py b/docs/source/conf.py index db2fdfc8d7..18e400168d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -98,10 +98,47 @@ def setup(app): "sphinx_copybutton", "nbsphinx", "sphinx_gallery.load_style", + "sphinx_design", + "sphinx_tags", ] +# to add tags to the documentation tutorials +tags_create_tags = True +tags_output_dir = "tags/" +tags_overview_title = "All tags" +tags_create_badges = True +tags_intro_text = "Tags on this page: " +tags_page_title = "Tags" +tags_page_header = "Pages with this tag: " +tags_index_head = "Tags in the documentation tutorials: " +tags_extension = ["md"] +tags_badge_colors = { + "zne": "primary", + "rem": "primary", + "shadows": "primary", + "cdr": "primary", + "pec": "primary", + "ddd": "primary", + "calibration": "primary", + "cirq": "secondary", + "bqskit": "secondary", + "braket": "secondary", + "pennylane": "secondary", + "qiskit": "secondary", + "stim": "secondary", + "qrack": "secondary", + "qibo": "secondary", + "ionq": "secondary", + "basic": "success", + "intermediate": "success", + "advanced": "success", +} + +# hide primary sidebar from the following pages +html_sidebars = {"apidoc": [], "changelog": [], "bibliography": []} + intersphinx_mapping = { - "python": ("https://docs.python.org/3.9", None), + "python": ("https://docs.python.org/3.10", None), "numpy": ("https://numpy.org/doc/stable/", None), "scipy": ("https://docs.scipy.org/doc/scipy/", None), # Cirq is no longer using sphinx docs so interlinking is not possible. @@ -196,10 +233,16 @@ def setup(app): r"https://doi\.org/.*", r"https://link\.aps\.org/doi/.*", r"https://www\.sciencedirect\.com/science/article/.*", + r"https://github.com/unitaryfund/mitiq/compare/.*", + r"https://github.com/unitaryfund/mitiq/projects/7", ] linkcheck_retries = 3 +linkcheck_anchors_ignore_for_url = [ + "https://github.com/unitaryfund/qrack/blob/main/README.md" +] + class ApsStyle(pybtex.style.formatting.unsrt.Style): """Style that mimicks APS journals.""" @@ -401,6 +444,7 @@ def get_incollection_template(self, e): "examples/quantum_simulation_scars_ibmq": "_static/qmbs_ibmq.png", "examples/zne_logical_rb_cirq_stim": "_static/mitiq_stim_logo.png", "examples/quantum_simulation_1d_ising": "_static/quantum_simulation.png", + "examples/cdr_qrack": "_static/cdr-qrack.png", # default images if no thumbnail is specified "examples/*": "_static/mitiq-logo.png", } diff --git a/docs/source/examples/bqskit.md b/docs/source/examples/bqskit.md index 04b4d71812..e9a399fcac 100644 --- a/docs/source/examples/bqskit.md +++ b/docs/source/examples/bqskit.md @@ -10,6 +10,9 @@ kernelspec: name: python3 --- +```{tags} bqskit, zne, intermediate +``` + # Improving the accuracy of BQSKit compiled circuits with error mitigation In this tutorial we describe how to use error mitigation capabilities from [Mitiq](https://mitiq.readthedocs.io/en/stable/) together with the compilation capabilities of [BQSKit](https://bqskit.lbl.gov/) {cite}`Patel_2022_ACM`, a compiler for quantum circuits. BQSKit stands for Berkeley Quantum Synthesis Toolkit and it allows one "to compile quantum programs to efficient physical circuits for any QPU". diff --git a/docs/source/examples/braket_mirror_circuit.md b/docs/source/examples/braket_mirror_circuit.md index ee122dfd7e..10e86c2fe3 100644 --- a/docs/source/examples/braket_mirror_circuit.md +++ b/docs/source/examples/braket_mirror_circuit.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} braket, zne, basic +``` + # Mitiq with Braket This notebook shows improved performance on a mirror circuit benchmark with zero-noise extrapolation on Rigetti Aspen-9 via Amazon Braket. diff --git a/docs/source/examples/calibration-tutorial.md b/docs/source/examples/calibration-tutorial.md index 83f401d298..06f9b15702 100644 --- a/docs/source/examples/calibration-tutorial.md +++ b/docs/source/examples/calibration-tutorial.md @@ -12,6 +12,9 @@ kernelspec: name: python3 --- +```{tags} calibration, zne, qiskit, basic +``` + # Breaking into error mitigation with Mitiq's calibration module diff --git a/docs/source/examples/cdr_qrack.md b/docs/source/examples/cdr_qrack.md new file mode 100644 index 0000000000..6f9aa5a2e5 --- /dev/null +++ b/docs/source/examples/cdr_qrack.md @@ -0,0 +1,190 @@ +--- +jupytext: + formats: ipynb,md:myst + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.16.3 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +```{tags} qiskit, cdr, qrack, cirq, basic +``` + +# CDR with Qrack as Near-Clifford Simulator + +In this tutorial, [Clifford Data Regression](../guide/cdr.md) (CDR) is used with [Qrack](https://qrack.readthedocs.io/en/latest/) and a Qiskit fake backend. + ++++ + +## Setup + +To start, relevant modules and libraries are imported. Please ensure that the following Python modules are installed: `mitiq`, `numpy`, `pyqrack`, `cirq`, `qiskit` + ++++ + +```{note} +In the code below the environmental variable, `QRACK_MAX_CPU_QB`, is set to `-1`. This enviroment variable sets the maximum on how many qubits can be allocated on a single QEngineCPU instance. More information can be found on the [Qrack README page](https://github.com/unitaryfund/qrack/blob/main/README.md#maximum-allocation-guard). +``` + +```{code-cell} +import numpy as np +import collections + +import os +import warnings +warnings.simplefilter("ignore", np.ComplexWarning) + +from pyqrack import QrackSimulator, QrackCircuit +os.environ["QRACK_MAX_CPU_QB"]="-1" + +import mitiq.interface.mitiq_qiskit +from mitiq.interface.mitiq_cirq import compute_density_matrix +from mitiq import cdr, Observable, PauliString + +import cirq + +from qiskit.providers.fake_provider import Fake5QV1 +``` + +## Sample Circuit + +This sample circuit includes Clifford gates (`H`, `CNOT`, `RX`) and non-Clifford gates (`RZ`). + +```{code-cell} +a, b = cirq.LineQubit.range(2) +circuit = cirq.Circuit( + cirq.H.on(a), # Clifford + cirq.H.on(b), # Clifford + cirq.rz(1.75).on(a), + cirq.rz(2.31).on(b), + cirq.CNOT.on(a, b), # Clifford + cirq.rz(-1.17).on(b), + cirq.rz(3.23).on(a), + cirq.rx(np.pi / 2).on(a), # Clifford + cirq.rx(np.pi / 2).on(b), # Clifford +) + +# CDR works better if the circuit is not too short. So we increase its depth. +circuit = 5 * circuit +print(circuit) +``` + +## Devices and Near-Clifford Simulator + +During CDR, near-Clifford representations of the original circuit are generated. Those near-Clifford representations are run with a near-Clifford simulator (Qrack Near-Clifford Simulator) and also on the quantum device (or noisy simulator, like Qiskit Fake Backend.) In this example, we also want to show how the unmitigated and mitigated results compare to the exact results, so we will use another simulator (Cirq Simulator) to generate the ideal results. + ++++ + +### Qrack Near-Clifford Simulator + +Especially when using CDR at scale, it is important to use an efficient Near-Clifford circuit simulator. In this example, Qrack will be configured and used as the Near-Clifford Simulator. The `qrack_simulate` method accepts a Cirq circuit and the number of shots as parameters. The Qrack simulator is then called and the expectation value for `00` is returned. + +Since CDR includes the generation of near-Clifford circuits representing the original circuit, a near-Clifford simulator is ideal for efficiently simulating the circuit. Again, this is particularly important when using CDR at scale. + +```{code-cell} +def qrack_simulate(circuit: cirq.Circuit, shots=1000) -> float: + """Returns the expectation value of 00 from the state prepared by the circuit + executed without noise by Qrack configured as a near-Clifford simulator. + """ + + # Cirq -> Qiskit circuit + qiskit_circ = mitiq.interface.mitiq_qiskit.to_qiskit(circuit) + + # Qiskit -> Qrack circuit + qcircuit = QrackCircuit.in_from_qiskit_circuit(qiskit_circ) + + # Setup the Qrack simulator and run it + qsim = QrackSimulator(qiskit_circ.width(), isStabilizerHybrid=True, isTensorNetwork=False, isSchmidtDecomposeMulti=False, isSchmidtDecompose=False, isOpenCL=False) + qcircuit.run(qsim) + + # Use shot measurements to return the expectation value of 00 + results = qsim.measure_shots(q=list(range(qiskit_circ.width())), s=shots) + results = dict(collections.Counter(results)) + for key, value in results.items(): + results[key] = value / shots + return results[0] +``` + +### Qiskit Fake Backend + +CDR requires the use of a quantum device or a noisy simulator. The near-Clifford circuits that are generated from the original circuit are executed on the quantum device or noisy simulator in order to compare against the simulated results. + +In this example, a Qiskit 5 Qubit Fake Backend is used. The [Fake5QV1](https://docs.quantum.ibm.com/api/qiskit/qiskit.providers.fake_provider.Fake5QV1) uses configurations and noise settings taken previously from the 5 qubit IBM Quantum Yorktown device. The `qiskit_noisy` function takes the Cirq circuit and uses Mitiq to change it into a Qiskit circuit. After adding measurements, the circuit is run on the fake backend. The expectation value for `00` is then returned. + +```{code-cell} +# Use Qiskit's Fave5QV1 as a noisy simulator +def qiskit_noisy(circuit: cirq.Circuit, shots=1000): + """Execute the input circuit and return the expectation value of |00..0><00..0|""" + + # Cirq -> Qiskit circuit + qiskit_circ = mitiq.interface.mitiq_qiskit.to_qiskit(circuit) + + # Add measurement gates to the circuit + qiskit_circ.measure_all() + + # Setup the fake backend and run the circuit + noisy_backend = Fake5QV1() + job = noisy_backend.run(qiskit_circ, shots=shots) + + # Use the resulting counts to return the expectation value of 00 + counts = job.result().get_counts() + ret_val = counts[qiskit_circ.num_qubits * "0"] / shots + return ret_val +``` + +### Cirq Simulator for exact result + +The `compute_density_matrix` is the Cirq density matrix simulator with a Mitiq wrapper. It is used to obtain the exact `00` expectation value. This is then used to determine the accuracy of the mitigated and unmitigated reuslts. + +```{code-cell} +def cirq_simulate(circuit: cirq.Circuit) -> np.ndarray: + """Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit + executed without depolarizing noise. + """ + res = compute_density_matrix(circuit, noise_level=(0.0,)) + return res[0, 0].real +``` + +## Executing CDR + +With the different executor functions defined for running the Qrack, Qiskit, and Cirq simulators, the `mitiq.cdr.execute_with_cdr` function can now be called. + +```{code-cell} +ideal_expval = cirq_simulate(circuit).round(5) +print(f"Ideal expectation value from Cirq Simulator: {ideal_expval:.3f}") + +unmitigated_expval = qiskit_noisy(circuit) +print(f"Unmitigated expectation value from Qiskit Fake backend: {unmitigated_expval:.3f}") + +mitigated_expval = cdr.execute_with_cdr( + circuit, + qiskit_noisy, + simulator=qrack_simulate, + random_state=0, +) +print(f"Mitigated expectation value with Mitiq CDR: {mitigated_expval:.3f}\n") + +``` + +## Conclusion +By learning from the near-Clifford circuits that resemble the original circuit, CDR is able to apply the noise information learned to the original circuit. From the example, you can see that the mitigated expectation value is closer to the ideal value. + +The improvement factor with CDR can be seen below to highlight the mitigated results that can be provided with this technique. + +```{code-cell} +unmitigated_error = abs(unmitigated_expval - ideal_expval) +mitigated_error = abs(mitigated_expval - ideal_expval) + +print(f"Unmitigated Error: {unmitigated_error:.3f}") +print(f"Mitigated Error: {mitigated_error:.3f}") + +improvement_factor = unmitigated_error / mitigated_error +print(f"Improvement factor with CDR: {improvement_factor:.2f}") +``` + +To learn more about CDR, please check the [User Guide](../guide/cdr.md) or the [ZNE and CDR with Cirq: 1D Ising Simulation](./quantum_simulation_1d_ising.md) example. If you have any questions, please do not hesitate to open a [Github discussion](https://github.com/unitaryfund/mitiq/discussions) or reach out to the Mitiq team on [Discord](http://discord.unitary.fund). diff --git a/docs/source/examples/cirq-ibmq-backends.md b/docs/source/examples/cirq-ibmq-backends.md index 90fb133e89..391f9d631f 100644 --- a/docs/source/examples/cirq-ibmq-backends.md +++ b/docs/source/examples/cirq-ibmq-backends.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, qiskit, basic +``` + # Error mitigation with Cirq on IBMQ backends diff --git a/docs/source/examples/combine_ddd_zne.md b/docs/source/examples/combine_ddd_zne.md index c907ee2907..9078ce108a 100644 --- a/docs/source/examples/combine_ddd_zne.md +++ b/docs/source/examples/combine_ddd_zne.md @@ -11,6 +11,8 @@ kernelspec: language: python name: python3 --- +```{tags} ddd, zne, cirq, intermediate +``` # Composing techniques: Digital Dynamical Decoupling and Zero Noise Extrapolation diff --git a/docs/source/examples/combine_rem_zne.md b/docs/source/examples/combine_rem_zne.md index 6676418d49..21a9c018db 100644 --- a/docs/source/examples/combine_rem_zne.md +++ b/docs/source/examples/combine_rem_zne.md @@ -11,6 +11,8 @@ kernelspec: language: python name: python3 --- +```{tags} rem, zne, cirq, intermediate +``` # Composing techniques: Readout Error Mitigation and Zero Noise Extrapolation diff --git a/docs/source/examples/ddd_on_ibmq_ghz.md b/docs/source/examples/ddd_on_ibmq_ghz.md index fa3c29a5db..9d432203e5 100644 --- a/docs/source/examples/ddd_on_ibmq_ghz.md +++ b/docs/source/examples/ddd_on_ibmq_ghz.md @@ -11,6 +11,8 @@ kernelspec: language: python name: python3 --- +```{tags} ddd, qiskit, basic +``` # Digital dynamical decoupling (DDD) with Qiskit on GHZ Circuits diff --git a/docs/source/examples/ddd_tutorial.md b/docs/source/examples/ddd_tutorial.md index 96ef6344ab..c2df395834 100644 --- a/docs/source/examples/ddd_tutorial.md +++ b/docs/source/examples/ddd_tutorial.md @@ -10,6 +10,8 @@ kernelspec: language: python name: python3 --- +```{tags} ddd, cirq, basic +``` # Digital dynamical decoupling (DDD) with Mirror Circuits diff --git a/docs/source/examples/examples.md b/docs/source/examples/examples.md index 499f00a5b7..adbd1c9406 100644 --- a/docs/source/examples/examples.md +++ b/docs/source/examples/examples.md @@ -1,6 +1,8 @@ # Examples -Below you can find a gallery of tutorials applying Zero Noise Extrapolation (ZNE), Probabilistic Error Cancellation (PEC), and Digital Dynamical Decoupling (DDD) with Mitiq: +Below you can find a gallery of tutorials applying Zero Noise Extrapolation (ZNE), Probabilistic Error Cancellation (PEC), Clifford Data Regression (CDR), and Digital Dynamical Decoupling (DDD), etc. with Mitiq. + +You can also search for an example utilizing a specific QEM technique, frontend, backend or difficulty level by clicking through the tags available [here.](tags.md) ```{nbgallery} ZNE Calibration with Qiskit @@ -32,8 +34,9 @@ Classical Shadows with Cirq: State Reconstruction and Observable Estimation DDD with Cirq: Mirror circuits DDD with Qiskit: GHZ circuits +CDR with Qrack as Near-Clifford Simulator GGI Summer School ZNE Hands-On Tutorial Composing techniques: REM + ZNE Composing techniques: DDD + ZNE The Mitiq paper code -``` +``` \ No newline at end of file diff --git a/docs/source/examples/ggi_summer_school_unsolved.md b/docs/source/examples/ggi_summer_school_unsolved.md index 85e67a7c8b..f636623033 100644 --- a/docs/source/examples/ggi_summer_school_unsolved.md +++ b/docs/source/examples/ggi_summer_school_unsolved.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, intermediate +``` + # Hands-on lab on error mitigation with Mitiq. +++ diff --git a/docs/source/examples/hamiltonians.md b/docs/source/examples/hamiltonians.md index 6987d7ccbe..25f17a4948 100644 --- a/docs/source/examples/hamiltonians.md +++ b/docs/source/examples/hamiltonians.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, advanced +``` + # Defining Hamiltonians as Linear Combinations of Pauli Strings This tutorial shows an example of using Hamiltonians defined as {class}`~cirq.PauliSum` objects or similar objects in other diff --git a/docs/source/examples/ibmq-backends.md b/docs/source/examples/ibmq-backends.md index 75fdaac3f4..2cd750bee2 100644 --- a/docs/source/examples/ibmq-backends.md +++ b/docs/source/examples/ibmq-backends.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, basic +``` + # Error mitigation on IBMQ backends with Qiskit @@ -97,7 +100,7 @@ def ibmq_executor(circuit: qiskit.QuantumCircuit, shots: int = 8192) -> float: ) # Run the circuit - job = backend.run(circuit, shots=shots) + job = backend.run(exec_circuit, shots=shots) # Convert from raw measurement counts to the expectation value counts = job.result().get_counts() diff --git a/docs/source/examples/layerwise-folding.md b/docs/source/examples/layerwise-folding.md index b2b081ddb9..6e5efd0a9c 100644 --- a/docs/source/examples/layerwise-folding.md +++ b/docs/source/examples/layerwise-folding.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, intermediate +``` + # ZNE with Qiskit: Layerwise folding diff --git a/docs/source/examples/learning-depolarizing-noise.md b/docs/source/examples/learning-depolarizing-noise.md index 2419e1c3be..0f2756dea8 100644 --- a/docs/source/examples/learning-depolarizing-noise.md +++ b/docs/source/examples/learning-depolarizing-noise.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, pec, advanced +``` + # Learning quasiprobability representations with a depolarizing noise model In this example, we demonstrate the workflow of learning quasiprobability representations of a `CNOT` gate with a depolarizing noise model, diff --git a/docs/source/examples/maxcut-demo.md b/docs/source/examples/maxcut-demo.md index 9450e3fc84..fed240089a 100644 --- a/docs/source/examples/maxcut-demo.md +++ b/docs/source/examples/maxcut-demo.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, advanced +``` + # Solving MaxCut with Mitiq-improved QAOA +++ diff --git a/docs/source/examples/molecular_hydrogen.md b/docs/source/examples/molecular_hydrogen.md index 95f45e2a52..defa5c55d1 100644 --- a/docs/source/examples/molecular_hydrogen.md +++ b/docs/source/examples/molecular_hydrogen.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, advanced +``` + # Estimating the potential energy surface of molecular Hydrogen with ZNE and OpenFermion In this example we apply zero-noise extrapolation (ZNE) to the estimation of the potential energy surface of molecular Hydrogen ($\rm H_2$). diff --git a/docs/source/examples/molecular_hydrogen_pennylane.md b/docs/source/examples/molecular_hydrogen_pennylane.md index 720ab88502..914f20c80b 100644 --- a/docs/source/examples/molecular_hydrogen_pennylane.md +++ b/docs/source/examples/molecular_hydrogen_pennylane.md @@ -10,6 +10,8 @@ kernelspec: language: python name: python3 --- +```{tags} cirq, pennylane, zne, advanced +``` # Estimating the potential energy surface of molecular Hydrogen with ZNE and PennyLane + Cirq diff --git a/docs/source/examples/pec_tutorial.md b/docs/source/examples/pec_tutorial.md index 299d9329b9..22b9b32ea5 100644 --- a/docs/source/examples/pec_tutorial.md +++ b/docs/source/examples/pec_tutorial.md @@ -11,6 +11,8 @@ kernelspec: name: python3 --- +```{tags} pec, cirq, qiskit, basic +``` # Probabilistic error cancellation (PEC) with Mirror Circuits This notebook shows probabilistic error cancellation (PEC) *Temme et al. PRL (2017)* {cite}`Temme_2017_PRL`, *Sun et al. PRLAppl (2021)* {cite}`Sun_2021_PRAppl`, *Zhang et al. NatComm (2020)* {cite}`Zhang_2020_NatComm`, improving performance of a mirror circuit benchmark on the `braket_dm` noisy simulator. diff --git a/docs/source/examples/pennylane-ibmq-backends.md b/docs/source/examples/pennylane-ibmq-backends.md index d68f447935..feb5ba6547 100644 --- a/docs/source/examples/pennylane-ibmq-backends.md +++ b/docs/source/examples/pennylane-ibmq-backends.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, pennylane, basic +``` + # Error mitigation with Pennylane on IBMQ backends In this tutorial we will cover how to use Mitiq in conjunction with [PennyLane](https://pennylane.ai/), and further how to run error-mitigated circuits on IBMQ backends. diff --git a/docs/source/examples/pyquil_demo.ipynb b/docs/source/examples/pyquil_demo.ipynb index 2f9b654df8..94ec38e5d6 100644 --- a/docs/source/examples/pyquil_demo.ipynb +++ b/docs/source/examples/pyquil_demo.ipynb @@ -1,5 +1,18 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "e564c033", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "```{tags} zne, intermediate\n", + "```" + ] + }, { "cell_type": "markdown", "id": "dac81d40", @@ -141,7 +154,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -239,7 +252,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] diff --git a/docs/source/examples/qibo-noisy-simulation.md b/docs/source/examples/qibo-noisy-simulation.md index 7cf805cb77..315d7f8bb0 100644 --- a/docs/source/examples/qibo-noisy-simulation.md +++ b/docs/source/examples/qibo-noisy-simulation.md @@ -1,7 +1,7 @@ --- jupytext: text_representation: - extension: .myst + extension: .md format_name: myst format_version: 0.13 jupytext_version: 1.11.1 @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qibo, zne, basic +``` + # Error mitigation with Qibo using noisy simulation In this tutorial we will cover how to use Mitiq to apply [Zero-Noise Extrapolation](../guide/zne.md) (ZNE) to a quantum program written using [Qibo](https://qibo.science/). diff --git a/docs/source/examples/quantum_simulation_1d_ising.md b/docs/source/examples/quantum_simulation_1d_ising.md index 093c9f60cc..4bca02beb9 100644 --- a/docs/source/examples/quantum_simulation_1d_ising.md +++ b/docs/source/examples/quantum_simulation_1d_ising.md @@ -11,11 +11,14 @@ kernelspec: name: python3 --- +```{tags} cdr, zne, cirq, advanced +``` + # Using ZNE and learning-based methods to mitigate the 1D transverse-longitudinal Ising model In this tutorial, we employ ZNE, CDR, and VNCDR mitigation techniques to address errors in the simulation of the 1-D Transverse-Longitudinal Ising model using Mitiq. It is important to note that the results presented here are not original, but rather an attempt to reproduce some of the findings outlined in the paper available at {cite}`Sopena_2023_Quantum`. -One of the primary applications of quantum computers is simulating dynamics in many-body systems. This is particularly significant because as the system size increases, the number of parameters grows exponentially. As a result, classical computers struggle to efficiently simulate such dynamics. However, we are currently in the Noisy Intermediate-Scale Quantum (NISQ) era, which means we lack the necessary resources for fault-tolerant quantum computing. Nevertheless, Quantum Error Mitigation techniques have been developed to address noise using minimal qubit resources. These techniques harness the power of classical computers to handle and mitigate quantum noise. In quantum simulation, our main interest is usually finding the average value of an observable. However, NISQ hardware can only provide us with noisy results. In mitigation techniques, we combine these noisy results with the computational power of classical computers to combat the noise. In this tutorial, we specifically utilize Zero Noise Extrapolation (ZNE), Corrected Dynamical Reduction (CDR), and Variational Noise-Corrected Dynamical Reduction (VNCDR) techniques to mitigate errors in the simulation of a 1-D Ising Hamiltonian. +One of the primary applications of quantum computers is simulating dynamics in many-body systems. This is particularly significant because as the system size increases, the number of parameters grows exponentially. As a result, classical computers struggle to efficiently simulate such dynamics. However, we are currently in the Noisy Intermediate-Scale Quantum (NISQ) era, which means we lack the necessary resources for fault-tolerant quantum computing. Nevertheless, Quantum Error Mitigation techniques have been developed to address noise using minimal qubit resources. These techniques harness the power of classical computers to handle and mitigate quantum noise. In quantum simulation, our main interest is usually finding the average value of an observable. However, NISQ hardware can only provide us with noisy results. In mitigation techniques, we combine these noisy results with the computational power of classical computers to combat the noise. In this tutorial, we specifically utilize Zero Noise Extrapolation (ZNE), Clifford Data Regression (CDR), and Variable-Noise Clifford Data Regression (vnCDR) techniques to mitigate errors in the simulation of a 1-D Ising Hamiltonian. The Hamiltonian for the quantum one-dimensional Ising model, with both transverse and longitudinal fields, can be expressed as follows: diff --git a/docs/source/examples/quantum_simulation_scars_ibmq.md b/docs/source/examples/quantum_simulation_scars_ibmq.md index 74409518da..e439df5cea 100644 --- a/docs/source/examples/quantum_simulation_scars_ibmq.md +++ b/docs/source/examples/quantum_simulation_scars_ibmq.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, advanced +``` + ```{code-cell} ipython3 import qiskit from qiskit import QuantumCircuit diff --git a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.json b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.json new file mode 100644 index 0000000000..f56bff5e12 --- /dev/null +++ b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.json @@ -0,0 +1,5010 @@ +{ + "cirq_type": "Circuit", + "moments": [ + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8480355463083987, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5431108811234443, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6869516405217995, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5501488883297712, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.630765913109793, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5531071857437787, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6082915032117944, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.557277055564494, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6009252833231602, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5650411624154018, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6048959861259817, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5807330531368797, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6256809297721176, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6200608562279073, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + }, + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + }, + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 8 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + }, + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 9 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + }, + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 10 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + }, + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 11 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + }, + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 12 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + }, + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 13 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7018517888261941, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 14 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.8188003288099811, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 15 + } + ] + } + ] + } + ] +} diff --git a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.pkl b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.pkl deleted file mode 100644 index dbab8bd75f..0000000000 Binary files a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_16qubits.pkl and /dev/null differ diff --git a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.json b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.json new file mode 100644 index 0000000000..e674fd33db --- /dev/null +++ b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.json @@ -0,0 +1,1326 @@ +{ + "cirq_type": "Circuit", + "moments": [ + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "HPowGate", + "exponent": 1.0, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.8410831566447878, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5642310853305742, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.6939407900644148, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.5824447005151139, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.665132726663069, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.6149709459449458, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + }, + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + }, + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + }, + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 1 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + }, + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 2 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + }, + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 3 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + }, + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 4 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + }, + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 5 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "ZZPowGate", + "exponent": 0.7133549430097101, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + }, + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 6 + } + ] + } + ] + }, + { + "cirq_type": "Moment", + "operations": [ + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 0 + } + ] + }, + { + "cirq_type": "GateOperation", + "gate": { + "cirq_type": "XPowGate", + "exponent": 0.7975562204945627, + "global_shift": 0.0 + }, + "qubits": [ + { + "cirq_type": "LineQubit", + "x": 7 + } + ] + } + ] + } + ] +} diff --git a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.pkl b/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.pkl deleted file mode 100644 index 22c31478db..0000000000 Binary files a/docs/source/examples/resources/rshadows-tutorial-1D_Ising_g=1_8qubits.pkl and /dev/null differ diff --git a/docs/source/examples/rshadows_tutorial.md b/docs/source/examples/rshadows_tutorial.md index b8c8ec6309..319cf80f46 100644 --- a/docs/source/examples/rshadows_tutorial.md +++ b/docs/source/examples/rshadows_tutorial.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} shadows, cirq, intermediate +``` + # Robust Shadow Estimation with Mitiq **Corresponding to:** Min Li (minl2@illinois.edu) @@ -77,9 +80,8 @@ num_qubits = 8 qubits: List[cirq.Qid] = cirq.LineQubit.range(num_qubits) if download_ising_circuits: - with open(f"{file_directory}/rshadows-tutorial-1D_Ising_g=1_{num_qubits}qubits.pkl", "rb") as file: - old_cirq_circuit = pickle.load(file) - circuit = cirq.Circuit(old_cirq_circuit.all_operations()) + with open(f"{file_directory}/rshadows-tutorial-1D_Ising_g=1_{num_qubits}qubits.json", "rb") as file: + circuit = cirq.read_json(json_text=file.read()) g = 1 # or user can import from tensorflow_quantum @@ -499,9 +501,8 @@ Import groud state of 1-D Ising model with periodic boundary condition num_qubits = 16 qubits = cirq.LineQubit.range(num_qubits) if download_ising_circuits: - with open(f"{file_directory}/rshadows-tutorial-1D_Ising_g=1_{num_qubits}qubits.pkl", "rb") as file: - old_cirq_circuit = pickle.load(file) - circuit = cirq.Circuit(old_cirq_circuit.all_operations()) + with open(f"{file_directory}/rshadows-tutorial-1D_Ising_g=1_{num_qubits}qubits.json", "rb") as file: + circuit = cirq.read_json(json_text=file.read()) g = 1 else: qbs = cirq.GridQubit.rect(num_qubits, 1) @@ -515,7 +516,7 @@ else: circuit = circuit.transform_qubits(qubit_map=qubit_map) ``` -Define obersevables lists as two point correlation functions between the first qubit and the rest of the qubits $\{\langle Z_0 Z_i\rangle\}_{0\geq i\leq n-1}$ +Define a list of observables as two point correlation functions between the first qubit and every other qubit $\{\langle Z_0 Z_i\rangle\}_{0\leq i\leq n-1}$. ```{code-cell} ipython3 diff --git a/docs/source/examples/scaling.md b/docs/source/examples/scaling.md index b58e381714..56b5aeee57 100644 --- a/docs/source/examples/scaling.md +++ b/docs/source/examples/scaling.md @@ -11,6 +11,8 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, basic +``` # Noise Scaling Methods In this tutorial we will compare two noise scaling methods available for use in [Zero-Noise Extrapolation](https://mitiq.readthedocs.io/en/stable/guide/zne.html) (ZNE): identity insertion and unitary folding. diff --git a/docs/source/examples/shadows_tutorial.md b/docs/source/examples/shadows_tutorial.md index f6798371df..42038321db 100644 --- a/docs/source/examples/shadows_tutorial.md +++ b/docs/source/examples/shadows_tutorial.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, shadows, intermediate +``` + # Classical Shadows Protocol with Cirq **Corresponding to:** Min Li (minl2@illinois.edu) diff --git a/docs/source/examples/simple-landscape-braket.md b/docs/source/examples/simple-landscape-braket.md index b0f7713526..cb7d2be4ad 100644 --- a/docs/source/examples/simple-landscape-braket.md +++ b/docs/source/examples/simple-landscape-braket.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} braket, zne, advanced +``` + # Using ZNE to compute the energy landscape of a variational circuit with Braket This tutorial shows an example in which the energy landscape for a two-qubit variational circuit is explored with and without error mitigation, using Amazon's [Braket](https://amazon-braket-sdk-python.readthedocs.io/en/latest/) as our frontend. diff --git a/docs/source/examples/simple-landscape-cirq.md b/docs/source/examples/simple-landscape-cirq.md index 724832e527..8b9f7fb273 100644 --- a/docs/source/examples/simple-landscape-cirq.md +++ b/docs/source/examples/simple-landscape-cirq.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, zne, advanced +``` + # Mitigating the energy landscape of a variational circuit with Mitiq +++ diff --git a/docs/source/examples/simple-landscape-pennylane.md b/docs/source/examples/simple-landscape-pennylane.md index f131527993..b1fda2f809 100644 --- a/docs/source/examples/simple-landscape-pennylane.md +++ b/docs/source/examples/simple-landscape-pennylane.md @@ -12,6 +12,9 @@ kernelspec: name: python3 --- +```{tags} pennylane, zne, advanced +``` + # Mitigating the energy landscape of a variational circuit with Mitiq and PennyLane This tutorial shows an example in which the energy landscape for a two-qubit variational circuit is explored diff --git a/docs/source/examples/simple-landscape-qiskit.md b/docs/source/examples/simple-landscape-qiskit.md index 740edcaa3a..9f07e8e9a1 100644 --- a/docs/source/examples/simple-landscape-qiskit.md +++ b/docs/source/examples/simple-landscape-qiskit.md @@ -12,6 +12,9 @@ kernelspec: name: python3 --- +```{tags} qiskit, zne, advanced +``` + # Using ZNE to compute the energy landscape of a variational circuit with Qiskit This tutorial shows an example in which the energy landscape for a two-qubit variational circuit is explored with and without error mitigation, using Qiskit as our frontend. diff --git a/docs/source/examples/tags.md b/docs/source/examples/tags.md new file mode 100644 index 0000000000..8162c30ed9 --- /dev/null +++ b/docs/source/examples/tags.md @@ -0,0 +1,22 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.16.1 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +orphan: true +--- + +# Tags + +```{toctree} +--- +maxdepth: 2 +--- +../../tags/tagsindex.md +``` \ No newline at end of file diff --git a/docs/source/examples/template.md b/docs/source/examples/template.md index 6b2f576f90..1e931a8616 100644 --- a/docs/source/examples/template.md +++ b/docs/source/examples/template.md @@ -14,7 +14,7 @@ kernelspec: # An example Jupyter Notebook This notebook is a demonstration of directly-parsing Jupyter Notebooks into -Sphinx using the MyST parser.[^download] +Sphinx using the MyST parser. ## Markdown @@ -164,4 +164,4 @@ nbsphinx_thumbnails = { "examples/{EXAMPLE_FILENAME_WITHOUT_.md}": "_static/{THUMBNAIL_FILENAME_WITH_EXTENSION}", "examples/hamiltonians": "_static/vqe-cirq-pauli-sum-mitigation-plot.png" } -``` \ No newline at end of file +``` diff --git a/docs/source/examples/vqe-pyquil-demo.ipynb b/docs/source/examples/vqe-pyquil-demo.ipynb index c4e033e222..b7c40d9c3d 100644 --- a/docs/source/examples/vqe-pyquil-demo.ipynb +++ b/docs/source/examples/vqe-pyquil-demo.ipynb @@ -33,6 +33,15 @@ "## Defining the quantum system using pyQuil" ] }, + { + "cell_type": "markdown", + "id": "b3340e15", + "metadata": {}, + "source": [ + "```{tags} zne, advanced\n", + "```" + ] + }, { "cell_type": "code", "execution_count": 1, diff --git a/docs/source/examples/zne-braket-ionq.md b/docs/source/examples/zne-braket-ionq.md index 9c9fdd4ece..db805a3791 100644 --- a/docs/source/examples/zne-braket-ionq.md +++ b/docs/source/examples/zne-braket-ionq.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} braket, zne, ionq, basic +``` + (label-zne-braket-ionq)= # Zero-noise extrapolation with Braket on the IonQ backend diff --git a/docs/source/examples/zne_logical_rb_cirq_stim.md b/docs/source/examples/zne_logical_rb_cirq_stim.md index 4d760dd0f5..62ead636a8 100644 --- a/docs/source/examples/zne_logical_rb_cirq_stim.md +++ b/docs/source/examples/zne_logical_rb_cirq_stim.md @@ -11,6 +11,9 @@ kernelspec: name: python3 --- +```{tags} cirq, stim, zne, intermediate +``` + # ZNE on Stim backend with Cirq: Logical randomized benchmarking circuits +++ diff --git a/docs/source/guide/cdr-1-intro.md b/docs/source/guide/cdr-1-intro.md index 916e26129b..6dc89ef32f 100644 --- a/docs/source/guide/cdr-1-intro.md +++ b/docs/source/guide/cdr-1-intro.md @@ -138,7 +138,7 @@ mitigated_expval = cdr.execute_with_cdr( compute_density_matrix, observable=obs, simulator=simulate, - seed=0, + random_state=0, ).real print(f"mitigated expectation value: {mitigated_expval:.2f}") ``` diff --git a/docs/source/guide/guide.md b/docs/source/guide/guide.md index 40528efa75..d75a3571c7 100644 --- a/docs/source/guide/guide.md +++ b/docs/source/guide/guide.md @@ -8,11 +8,12 @@ core-concepts.md zne.md pec.md cdr.md -shadows.md ddd.md +lre.md rem.md qse.md pt.md +shadows.md error-mitigation.md glossary.md ``` diff --git a/docs/source/guide/lre-1-intro.md b/docs/source/guide/lre-1-intro.md new file mode 100644 index 0000000000..8e195ad1ef --- /dev/null +++ b/docs/source/guide/lre-1-intro.md @@ -0,0 +1,151 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.1 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# How do I use LRE? + +LRE works in two main stages: generate noise-scaled circuits via layerwise scaling, and apply inference to resulting measurements post-execution. + +This workflow can be executed by a single call to {func}`.execute_with_lre`. +If more control is needed over the protocol, Mitiq provides {func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients` to handle the first and second steps respectively. + +```{danger} +LRE is currently compatible with quantum programs written using `cirq`. +Work on making this technique compatible with other frontends is ongoing. 🚧 +``` + +## Problem Setup + +To demonstrate the use of LRE, we'll first define a quantum circuit, and a method of executing circuits for demonstration purposes. + +For simplicity, we define a circuit whose unitary compiles to the identity operation. +Here we will use a randomized benchmarking circuit on a single qubit, visualized below. + +```{code-cell} ipython3 +from mitiq import benchmarks + + +circuit = benchmarks.generate_rb_circuits(n_qubits=1, num_cliffords=3)[0] + +print(circuit) +``` + +We define an [executor](executors.md) which simulates the input circuit subjected to depolarizing noise, and returns the probability of measuring the ground state. +By altering the value for `noise_level`, ideal and noisy expectation values can be obtained. + +```{code-cell} ipython3 +from cirq import DensityMatrixSimulator, depolarize + + +def execute(circuit, noise_level=0.025): + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix + return rho[0, 0].real +``` + +Compare the noisy and ideal expectation values: + +```{code-cell} ipython3 +noisy = execute(circuit) +ideal = execute(circuit, noise_level=0.0) +print(f"Error without mitigation: {abs(ideal - noisy) :.5f}") +``` + +## Apply LRE directly + +With the circuit and executor defined, we just need to choose the polynomial extrapolation degree as well as the fold multiplier. + +```{code-cell} ipython3 +from mitiq.lre import execute_with_lre + + +degree = 2 +fold_multiplier = 3 + +mitigated = execute_with_lre( + circuit, + execute, + degree=degree, + fold_multiplier=fold_multiplier, +) + + +print(f"Error with mitigation (LRE): {abs(ideal - mitigated):.{3}}") +``` + +As you can see, the technique is extremely simple to apply, and no knowledge of the hardware/simulator noise is required. + +## Step by step application of LRE + +In this section we demonstrate the use of {func}`.multivariate_layer_scaling` and {func}`.multivariate_richardson_coefficients` for those who might want to inspect the intermediary circuits, and have more control over the protocol. + +### Create noise-scaled circuits + +We start by creating a number of noise-scaled circuits which we will pass to the executor. + +```{code-cell} ipython3 +from mitiq.lre.multivariate_scaling import multivariate_layer_scaling + + +noise_scaled_circuits = multivariate_layer_scaling(circuit, degree, fold_multiplier) +num_scaled_circuits = len(noise_scaled_circuits) + +print(f"total number of noise-scaled circuits for LRE = {num_scaled_circuits}") +print( + f"Average circuit depth = {sum(len(circuit) for circuit in noise_scaled_circuits) / num_scaled_circuits}" +) +``` + +As you can see, the noise scaled circuits are on average much longer than the original circuit. +An example noise-scaled circuit is shown below. + +```{code-cell} ipython3 +noise_scaled_circuits[3] +``` + +With the many noise-scaled circuits in hand, we can run them through our executor to obtain the expectation values. + +```{code-cell} ipython3 +noise_scaled_exp_values = [ + execute(circuit) for circuit in noise_scaled_circuits +] +``` + +### Classical inference + +The penultimate step here is to fetch the coefficients we'll use to combine the noisy data we obtained above. +The astute reader will note that we haven't defined or used a `degree` or `fold_multiplier` parameter, and this is where they are both needed. + +```{code-cell} ipython3 +from mitiq.lre.inference import multivariate_richardson_coefficients + + +coefficients = multivariate_richardson_coefficients( + circuit, + fold_multiplier=fold_multiplier, + degree=degree, +) +``` + +Each noise scaled circuit has a coefficient of linear combination and a noisy expectation value associated with it. + +### Combine the results + +```{code-cell} ipython3 +mitigated = sum( + exp_val * coeff + for exp_val, coeff in zip(noise_scaled_exp_values, coefficients) +) +print(f"Error with mitigation (LRE): {abs(ideal - mitigated):.{3}}") +``` + +As you can see we again see a nice improvement in the accuracy using a two stage application of LRE. diff --git a/docs/source/guide/lre-2-use-case.md b/docs/source/guide/lre-2-use-case.md new file mode 100644 index 0000000000..12b1d13951 --- /dev/null +++ b/docs/source/guide/lre-2-use-case.md @@ -0,0 +1,35 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.10.3 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + +# When should I use LRE? + +## Advantages + +Just as in ZNE, LRE can also be applied without a detailed knowledge of the underlying noise model as the effectiveness of the technique depends on the choice of scale factors. +Thus, LRE is useful in scenarios where tomography is impractical. + +The sampling overhead is flexible wherein the cost can be reduced by using larger values for the fold multiplier (used to +create the noise-scaled circuits) or by chunking a larger circuit to fold groups of layers of circuits instead of each one individually. + +## Disadvantages + +When using a large circuit, the number of noise scaled circuits grows polynomially such that the execution time rises because we require the sample matrix to be a square matrix (more details in the [theory](lre-5-theory.md) section). + +When reducing the sampling cost by using a larger fold multiplier, the bias for polynomial extrapolation increases as one moves farther away from the zero-noise limit. + +Chunking a large circuit with a lower number of chunks to reduce the sampling cost can reduce the performance of LRE. +In ZNE parlance, this is equivalent to local folding faring better than global folding in LRE when we use a higher number of chunks in LRE. + +```{attention} +We are currently investigating the issue related to the performance of chunking large circuits. +``` diff --git a/docs/source/guide/lre-5-theory.md b/docs/source/guide/lre-5-theory.md new file mode 100644 index 0000000000..2fa960bf27 --- /dev/null +++ b/docs/source/guide/lre-5-theory.md @@ -0,0 +1,95 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.4 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# What is the theory behind LRE? + +Similar to [ZNE](zne.md), LRE works in two steps: + +- **Step 1:** Intentionally create multiple noise-scaled but logically equivalent circuits by scaling each layer or chunk of the input circuit through unitary folding. + +- **Step 2:** Extrapolate to the noiseless limit using multivariate richardson extrapolation. + +The noise-scaled circuits in ZNE are scaled by the user choosing which layers of the input circuit to fold whereas in LRE +each noise-scaled circuit scales the layers in the input circuit in a specific pattern. +LRE leverages the flexible configuration space of layerwise unitary folding, allowing for a more nuanced mitigation of errors by treating the noise level of each layer of the quantum circuit as an independent variable. + +## Step 1: Create noise-scaled circuits + +The goal is to create noise-scaled circuits of different depths where the layers in each circuit are scaled in a specific pattern as a result of [unitary folding](zne-5-theory.md). +This pattern is described by the vector of scale factor vectors which are generated after the fold multiplier and degree for multivariate Richardson extrapolation are chosen. + +Suppose we're interested in the value of some observable of a circuit $C$ that has $l$ layers. +For each layer $0 \leq L \leq l$ we can choose a scale factor for how much to scale that particular layer. +Thus a vector $\lambda \in \mathbb{R}^l_+$ corresponds to a folding configuration where $\lambda_0$ corresponds to the scale factor for the first layer, and $\lambda_{l - 1}$ is the scale factor to apply on the circuits final layer. + +Fix the number of noise-scaled circuits we wish to generate at $M\in\mathbb{N}$. +Define $\Lambda = (λ_1, λ_2, \ldots, λ_M)^T$ to be the collection of scale factors and let $(C_{λ_1}, C_{λ_2}, \ldots, C_{λ_M})^T$ denote the noise-scaled circuits corresponding to each scale factor. + +After $d$ is fixed as the degree of the multivariate polynomial, we define $M_j(λ_i, d)$ to be the terms in the polynomial arranged in increasing order. +In general, the number of monomial terms with $l$ variables up to degree $d$ can be determined +through the [stars and bars method](https://en.wikipedia.org/wiki/Stars_and_bars_%28combinatorics%29). + +For example, if $C$ has 2 layers, the degree of the extrapolating polynomial is 2, the basis of monomials contains 6 terms: $\{1, λ_1, λ_2, {λ_1}^2, λ_1 \cdot λ_2, {λ_2}^2 \}$. + +$$ +\text{total number of terms in the monomial basis with max degree } d = \binom{d + l}{d} +$$ + +As the choice for the degree of the extrapolating polynomial is 2, we search for the number of terms with total degree 2 using the following formula: + +$$ +\text{number of terms in the monomial basis with total degree } d = \binom{d + l - 1}{d} +$$ + +Terms with total degree 2 are 3 calculated by $\binom{2 + 2 -1}{2} = 3$ and correspond to $\{{λ_1}^2, λ_1 \cdot λ_2, {λ_2}^2 \}$. + +Similarly, number of terms with total degree 1 and 0 can be calculated as $\binom{1 + 2 -1}{1} = 2:\{λ_1, λ_2\}$ and $\binom{0 + 2 -1}{0}= 1: \{1\}$ respectively. + +These terms in the monomial basis define the rows of the square sample matrix as shown below: + +$$ +\mathbf{A}(\Lambda, d) = +\begin{bmatrix} + M_1(λ_1, d) & M_2(λ_1, d) & \cdots & M_N(λ_1, d) \\ + M_1(λ_2, d) & M_2(λ_2, d) & \cdots & M_N(λ_2, d) \\ + \vdots & \vdots & \ddots & \vdots \\ + M_1(λ_N, d) & M_2(λ_N, d) & \cdots & M_N(λ_N, d) +\end{bmatrix} +$$ + +For our example circuit of $l=2$ and $d=2$, each row defined by the generic monomial terms $\{M_1(λ_i, d), M_2(λ_i, d), \ldots, M_N(λ_i, d)\}$ in the sample matrix $\mathbf{A}$ will instead be replaced by $\{1, λ_1, λ_2, {λ_1}^2, λ_1 \cdot λ_2, {λ_2}^2 \}$. + +Here, each monomial term in the sample matrix $\mathbf{A}$ is then evaluated using the values in the scale factor vectors. In Step 2, this sample matrix will be utilized to obtain our mitigated expectation value. + +## Step 2: Extrapolate to the noiseless limit + +Each noise scaled circuit $C_{λ_i}$ has an expectation value $\langle O(λ_i) \rangle$ associated with it such that we can define a vector of the noisy expectation values $z = (\langle O(λ_1) \rangle, \langle O(λ_2) \rangle, \ldots, \langle O(λ_M)\rangle)^T$. +These values can then be combined via a linear combination to estimate the ideal value $variable$. + +$$ +O_{\mathrm{LRE}} = \sum_{i=1}^{M} \eta_i \langle O(λ_i) \rangle. +$$ + +Finding the coefficients in the linear combination becomes a problem solvable through a system of linear equations $\mathbf{A} c = z$ where $c$ is the coefficients vector $(\eta_1, \eta_2, \ldots, \eta_N)^T$, $z$ is the vector of the noisy expectation values and $\mathbf{A}$ is the sample matrix evaluated using the values in the scale factor vectors. + +The [general multivariate Lagrange interpolation polynomial](https://www.siam.org/media/wkvnvame/a_simple_expression_for_multivariate.pdf) is defined by a new matrix $\mathbf{B}_i$ obtained by replacing the $i$-th row of the sample matrix $\mathbf{A}$ with monomial terms evaluated using the generic variable λ. Thus, matrix $\mathbf{B}_i$ represents an interpolating polynomial in variable λ of degree $d$. As we only need to find the noiseless expectation value, we can skip calculating the full vector of linear combination coefficients if we use the [Lagrange interpolation formula](https://files.eric.ed.gov/fulltext/EJ1231189.pdf) evaluated at $λ = 0$ i.e. the zero-noise limit. + +To get the matrix $\mathbf{B}_i(\mathbf{0})$, replace the $i$-th row of the sample matrix $\mathbf{A}$ by $\mathbf{e}_i=(1, 0, \ldots, 0)$ where except $M_1(0, d) = 1$ all the other monomial terms are zero when $λ=0$. + +$$ +O_{\rm LRE} = \sum_{i=1}^M \langle O (\boldsymbol{\lambda}_i)\rangle \frac{\det \left(\mathbf{B}_i (\boldsymbol{0}) \right)}{\det \left(\mathbf{A}\right)} +$$ + +To summarize, based on a user's choice of degree of extrapolating polynomial for some circuit, expectation values from noise scaled circuits created in a specific pattern along with multivariate Lagrange interpolation of the sample matrix evaluated using the scale factor vectors are used to find error mitigated expectation value. + +Additional details on the LRE functionality are available in the [API-doc](https://mitiq.readthedocs.io/en/stable/apidoc.html#module-mitiq.lre.multivariate_scaling.layerwise_folding). diff --git a/docs/source/guide/lre.md b/docs/source/guide/lre.md new file mode 100644 index 0000000000..b9a54e1218 --- /dev/null +++ b/docs/source/guide/lre.md @@ -0,0 +1,28 @@ +```{warning} +The user guide for LRE in Mitiq is currently under construction. +``` + +# Layerwise Richardson Extrapolation + +Layerwise Richardson Extrapolation (LRE), an error mitigation technique, introduced in +{cite}`Russo_2024_LRE` extends the ideas found in ZNE by allowing users to create multiple noise-scaled variations of the input +circuit such that the noiseless expectation value is extrapolated from the execution of each +noisy circuit. + +Layerwise Richardson Extrapolation (LRE), an error mitigation technique, introduced in +{cite}`Russo_2024_LRE` works by creating multiple noise-scaled variations of the input +circuit such that the noiseless expectation value is extrapolated from the execution of each +noisy circuit (see the section [What is the theory behind LRE?](lre-5-theory.md)). Compared to +Zero-Noise Extrapolation, this technique treats the noise in each layer of the circuit +as an independent variable to be scaled and then extrapolated independently. + +You can get started with LRE in Mitiq with the following sections of the user guide: + +```{toctree} +--- +maxdepth: 1 +--- +lre-1-intro.md +lre-2-use-case.md +lre-5-theory.md +``` diff --git a/docs/source/guide/pt-1-intro.md b/docs/source/guide/pt-1-intro.md index 4e83083dd4..b2f6199266 100644 --- a/docs/source/guide/pt-1-intro.md +++ b/docs/source/guide/pt-1-intro.md @@ -4,7 +4,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.11.1 + jupytext_version: 1.16.1 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -14,8 +14,7 @@ kernelspec: # How do I use PT? ```{admonition} Warning: -Pauli Twirling in Mitiq is still under construction. This users guide will change in the future -after some utility functions are introduced. +This user guide is still under construction and may change in the near future. ``` As with all techniques, PT is compatible with any frontend supported by Mitiq: @@ -26,20 +25,22 @@ import mitiq mitiq.SUPPORTED_PROGRAM_TYPES.keys() ``` +In this first section, we see how to use PT in Mitiq, starting from a circuit of interest. + ++++ ## Problem setup -We first define the circuit of interest. In this example, the circuit has -two CNOT gates and a CZ gate. We can see that when we apply Pauli Twirling, -we will generate +We first define the circuit, which in this example contains Hadamard (H), CNOT, and CZ gates. ```{code-cell} ipython3 -from cirq import LineQubit, Circuit, CZ, CNOT +from cirq import LineQubit, Circuit, CZ, CNOT, H -a, b, c, d = LineQubit.range(4) +q0, q1, q2, q3 = LineQubit.range(4) circuit = Circuit( - CNOT.on(a, b), - CZ.on(b, c), - CNOT.on(c, d), + H(q0), + CNOT.on(q0, q1), + CZ.on(q1, q2), + CNOT.on(q2, q3), ) print(circuit) @@ -50,56 +51,91 @@ the circuit on a noisy simulator, and returns the probability of the ground state. See the [Executors](executors.md) section for more information on how to define more advanced executors. +During execution by the simulator, a coherent error is introduced by applying a +rotation around the X-axis (Rx gate) to each output of any 2-qubit gate in the circuit of interest. + +For the sake of this example executed by a simulator, we set the noise level to be proportional to the angle of the Rx rotation. + +This noise model is well-suited to highlight the effect of Pauli Twirling, +which is a technique that transforms coherent noise into incoherent noise. + ```{code-cell} ipython3 -import numpy as np -from cirq import DensityMatrixSimulator, amplitude_damp -from mitiq.interface import convert_to_mitiq +from numpy import pi +from cirq import CircuitOperation, CXPowGate, CZPowGate, DensityMatrixSimulator, Rx +from cirq.devices.noise_model import GateSubstitutionNoiseModel -def execute(circuit, noise_level=0.1): - """Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit - executed with amplitude damping noise. +def get_noise_model(noise_level: float) -> GateSubstitutionNoiseModel: + """Substitute each CZ and CNOT gate in the circuit + with the gate itself followed by an Rx rotation on the output qubits. """ - # Replace with code based on your frontend and backend. - mitiq_circuit, _ = convert_to_mitiq(circuit) - noisy_circuit = mitiq_circuit.with_noise(amplitude_damp(gamma=noise_level)) - rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix - return rho[0, 0].real + rads = pi / 2 * noise_level + def noisy_c_gate(op): + if isinstance(op.gate, (CZPowGate, CXPowGate)): + return CircuitOperation( + Circuit( + op.gate.on(*op.qubits), + Rx(rads=rads).on_each(op.qubits), + ).freeze()) + return op + + return GateSubstitutionNoiseModel(noisy_c_gate) + +def execute(circuit: Circuit, noise_level: float): + """Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit.""" + return ( + DensityMatrixSimulator(noise=get_noise_model(noise_level=noise_level)) + .simulate(circuit) + .final_density_matrix[0, 0] + .real + ) ``` The [executor](executors.md) can be used to evaluate noisy (unmitigated) expectation values. ```{code-cell} ipython3 -# Compute the expectation value of the |0><0| observable. -noisy_value = execute(circuit) +# Set the intensity of the noise +NOISE_LEVEL = 0.1 + +# Compute the expectation value of the |0><0| observable +# in both the noiseless and the noisy setup ideal_value = execute(circuit, noise_level=0.0) -print(f"Error without mitigation: {abs(ideal_value - noisy_value) :.3}") +noisy_value = execute(circuit, noise_level=NOISE_LEVEL) + +print(f"Error without twirling: {abs(ideal_value - noisy_value) :.3}") ``` ## Apply PT -Pauli Twirling can be easily implemented with the function -{func}`.pauli_twirl_circuit()`. +PT can be applied by first generating twirled variants of the circuit with the function +{func}`.generate_pauli_twirl_variants` from the `mitiq.pt` module, +and then averaging over the results obtained by executing those variants. -```{code-cell} ipython3 -from mitiq import pt -mitigated_result = pt.pauli_twirl_circuit( - circuit=circuit, -) -``` +The more variants are generated and averaged over, the more visible the results of PT are. +In this example we generate 5 twirled variants of the circuit, by setting the `num_circuits` +argument of the function {func}`.generate_pauli_twirl_variants` (default value is 10.) ```{code-cell} ipython3 -# print(f"Error with mitigation (PT): {abs(ideal_value - mitigated_result) :.3}") +from functools import partial +import numpy as np +from mitiq import Executor +from mitiq.pt import generate_pauli_twirl_variants + +# Generate twirled circuits +NUM_TWIRLED_VARIANTS = 5 +twirled_circuits = generate_pauli_twirl_variants(circuit, num_circuits=NUM_TWIRLED_VARIANTS) +# Average results executed over twirled circuits +pt_vals = Executor(partial(execute, noise_level=NOISE_LEVEL)).evaluate(twirled_circuits) +mitigated_result = np.average(pt_vals) + +print(f"Error with twirling: {abs(ideal_value - mitigated_result) :.3}") ``` -Here we observe that the application of PT does not reduce the estimation error when compared -to the unmitigated result. The intended effect was to only tailor the noise. +The idea behind Pauli Twirling is that it leaves the effective logical circuit unchanged, while tailoring the noise into stochastic Pauli errors. ```{admonition} Note: -PT is designed to transform the noise simulated in this example, -but it should not be expected to always be a positive effect. -In this sense, it is more of a noise tailoring technique, designed -to be composed with other techniques rather than an error mitigation -technique in and of itself. +Pauli Twirling is designed to transform noise, such as the coherent noise simulated in the example above, +but it should not be expected to always have a positive effect. In this sense, it is more of a noise tailoring technique, +designed to be composed with other techniques rather than an error mitigation technique in itself. ``` +++ diff --git a/docs/source/guide/qse-1-intro.md b/docs/source/guide/qse-1-intro.md index 0d0dc7e3ef..ee38520b92 100644 --- a/docs/source/guide/qse-1-intro.md +++ b/docs/source/guide/qse-1-intro.md @@ -18,15 +18,16 @@ Here we show how to use QSE by means of a simple example. To use QSE, we call `qse.execute_with_qse()` with five “ingredients”: - A quantum circuit to prepare a state -- A quantum computer or noisy simulator to return a QuantumResult from +- A quantum computer or noisy simulator which returns a {class}`.QuantumResult` - An observable we wish to compute the error mitigated expectation value -- A list of Check Operators which can be stabilizers, symmetries or anything else. -- A Code Hamiltonian which defines the state with the least amount of errors +- A list of "check operators" which can be stabilizers, symmetries, or anything else. +- A "code Hamiltonian" which defines the state with the least amount of errors ## 1. Define a quantum circuit The quantum circuit can be specified as any quantum circuit supported by Mitiq. -In the next cell we define (as an example) a quantum circuit that prepares the logical 0 state in the [[5,1,3]] code. For simplicity, we use Gram Schmidt orthogonalization to form a unitary matrix that we use as a gate to prepare our state. +In the next cell we define (as an example) a quantum circuit that prepares the logical 0 state in the [[5,1,3]] code. +For simplicity, we use Gram Schmidt orthogonalization to form a unitary matrix that we use as a gate to prepare our state. ```{code-cell} ipython3 def prepare_logical_0_state_for_5_1_3_code(): @@ -104,13 +105,11 @@ def prepare_logical_0_state_for_5_1_3_code(): circuit.append(g(*qubits)) return circuit - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() ``` ## 2. Define an executor -We define an [executor](executors.md) function which inputs a circuit and returns a `QuantumResult`. Here for sake of example we use a simulator that adds single-qubit depolarizing noise after each moment and returns the final density matrix. +We define an [executor](executors.md) function which inputs a circuit and returns a `QuantumResult`. +Here for sake of example we use a simulator that adds single-qubit depolarizing noise after each moment and returns the final density matrix. ```{code-cell} ipython3 from typing import List @@ -127,14 +126,11 @@ def execute_with_depolarized_noise(circuit: QPROGRAM) -> np.ndarray: noise_model_function=cirq.depolarize, noise_level=(0.01,), ) - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise ``` ## 3. Observable -We define an observable in the code subspace: $O = ZZZZZ$. As an example, assume that we wish to compute the expectation value $Tr[\rho O]$ of the observable O. +We define an observable in the code subspace: $O = ZZZZZ$. +As an example, assume that we wish to compute the expectation value $\mathrm{tr}[\rho O]$ of the observable $O$. ```{code-cell} ipython3 def get_observable_in_code_space(observable: List[cirq.PauliString]): @@ -152,14 +148,12 @@ def get_observable_in_code_space(observable: List[cirq.PauliString]): observable_in_code_space *= 0.5 * Observable(FIVE_I, g) return observable_in_code_space -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise - observable = get_observable_in_code_space(PauliString("ZZZZZ")) ``` ## 4. Check Operators and Code Hamiltonian -We then assign the check operators as a list of pauli strings, and the code Hamiltonian as an Observable for the [[5,1,3]] code. The check operators of the [[5,1,3]] code are simply the expansion of the code’s 4 generators: $[XZZXI, IXZZX, XIXZZ, ZXIXZ]$ + +We then assign the check operators as a list of pauli strings, and the code Hamiltonian as an Observable for the [[5,1,3]] code. +The check operators of the [[5,1,3]] code are simply the expansion of the code’s 4 generators: $[XZZXI, IXZZX, XIXZZ, ZXIXZ]$ ```{code-cell} ipython3 def get_5_1_3_code_check_operators_and_code_hamiltonian() -> tuple: @@ -195,19 +189,11 @@ def get_5_1_3_code_check_operators_and_code_hamiltonian() -> tuple: ] Hc = Observable(*negative_Ms_as_pauliStrings) return Ms_as_pauliStrings, Hc - - -def demo_qse(): - circuit = prepare_logical_0_state_for_5_1_3_code() - executor = execute_with_depolarized_noise - observable = get_observable_in_code_space(PauliString("ZZZZZ")) - check_operators, code_hamiltonian = get_5_1_3_code_check_operators_and_code_hamiltonian() ``` - ## Run QSE -Now we can run QSE. We first compute the noiseless result then the noisy result to compare to the mitigated result from QSE. +With everything defined, we can now run QSE in full. ```{code-cell} ipython3 def demo_qse(): diff --git a/docs/source/guide/qse-3-options.md b/docs/source/guide/qse-3-options.md index 0f3b1bb77d..5baec9348f 100644 --- a/docs/source/guide/qse-3-options.md +++ b/docs/source/guide/qse-3-options.md @@ -11,20 +11,22 @@ kernelspec: name: python3 --- - # What additional options are available in QSE? -In addition to the necessary ingredients already discussed in [How do I use QSE?](qse-1-intro.md), there are a few additional options included in the implementation. +In addition to the necessary ingredients already discussed in [How do I use QSE?](qse-1-intro.md), there are a few additional options included in the implementation. ## Caching Pauli Strings to Expectation Values -```{warning} -The cache object is modified in place. -``` Specifically, in order to save runtime, the QSE implementation supports the use of a cache that maps pauli strings to their expectation values. This is taken as an additional parameter in the [`execute_with_qse`](https://mitiq.readthedocs.io/en/stable/apidoc.html#mitiq.qse.qse.execute_with_qse) function. -The inclusion of the cache significantly speeds up the runtime and avoids the need for re-computation of already computed values. -Furthermore, it is important to note that the cache gets modified in place, so the user can pass the same cache object to `execute_with_qse` and avoid regenerating the cache unnecessarily , e.g. if the noise model is the same from one execution to another. +```{warning} +The cache object is modified in place when passing it to `execute_with_qse`. +``` + +The inclusion of the cache significantly speeds up the runtime and avoids the need for re-computation of already computed values. +Furthermore, since the cache is modified in place, it can be reused as long as the noise model remains the same. ## Requirements for Check Operators -It is also important to note that when specifying the check (or excitation) operators for the execution, it is not necessary to specify the full exponential number of operators. As many or as few operators can be specified. The tradeoff is the fidelity of the projected state. +When specifying the check operators, it is **not** necessary to specify the full exponential number of operators. +As many or as few operators can be specified. +The tradeoff is the fidelity of the projected state. diff --git a/docs/source/guide/qse-5-theory.md b/docs/source/guide/qse-5-theory.md index a95196a6d2..6940f4cc4d 100644 --- a/docs/source/guide/qse-5-theory.md +++ b/docs/source/guide/qse-5-theory.md @@ -13,18 +13,24 @@ kernelspec: # What is the theory behind QSE? -We implement the subspace expansion method introduced by McClean et al {cite}`McClean_2020_NatComm` +We implement the subspace expansion method introduced by McClean et al {cite}`McClean_2020_NatComm`. -Subspace expansion as an error mitigation scheme uses a set of operators, called the check operators, to expand around the output state, then search this subspace for the state with the lowest error. When used in conjunction with a quantum code, a subset of the stabilizer group can be used to expand around the output state. If the full stabilizer group is used, subspace expansion is equivalent to projecting the final state of the computation onto the codeword subspace. +Subspace expansion as an error mitigation scheme uses a set of operators, called the check operators, to define a subspace surrounding the output state. +The protocol then involves searching this subspace for the state with the lowest error. -The check operators can also include other operators outside the stabilizers group. For instance, if the final state of the computation is known to have some symmetry, including the symmetry operator as a check operator is useful in correcting errors that don’t respect the symmetry. In our code implementation, we allow the user to input the set of check operators, which can be stabilizers, symmetries, or anything else. +```{tip} +When used in conjunction with a quantum code, a subset of the stabilizer group can be used to expand around the output state. +If the full stabilizer group is used, subspace expansion is equivalent to projecting the final state of the computation onto the codeword subspace. +``` -Let $| \Psi \rangle$ be the state we wish to prepare on the quantum computer and $M_i$ be the set of check operators such that $ M_i | \Psi \rangle = | \Psi \rangle$. Furthermore, let $\rho$ represent the density matrix of the actual final state prepared on the device. Due to errors, $\rho \neq | \Psi \rangle \langle \Psi |$. However, we would like to use our knowledge about the state $| \Psi \rangle$ encoded in the check operators to mitigate the errors in $\rho$. +The check operators can also include other operators outside the stabilizers group. For instance, if the final state of the computation is known to have some symmetry, including the symmetry operator as a check operator is useful in correcting errors that don’t respect the symmetry. In our code implementation, we allow the user to input the set of check operators, which can be stabilizers, symmetries, or anything else. -As mentioned, the main idea is to expand around $\rho$ using the check operators, and then search this subspace for the state with least amount of error. The state with the least amount of energy is defined as the state in the subspace that minimize $\mathrm H = - \sum_i M_i$. Thus the full procedure can be formulated as follows, -$\min_{{c_i}} \text{Tr}[\bar P_c \rho \bar P_c^\dagger \mathrm H ]$ subject to the constrain $\text{Tr}[\bar P_c \rho \bar P_c^\dagger] = 1$ with $\bar P_c = \sum c_i M_i$. +Let $| \Psi \rangle$ be the state we wish to prepare on the quantum computer and $M_i$ be the set of check operators such that $ M_i | \Psi \rangle = | \Psi \rangle$. Furthermore, let $\rho$ represent the density matrix of the actual final state prepared on the device. Due to errors, $\rho \neq | \Psi \rangle \langle \Psi |$. However, we would like to use our knowledge about the state $| \Psi \rangle$ encoded in the check operators to mitigate the errors in $\rho$. -As is turned out, this minimization problem can be mapped to the following generalized eigenvalue problem: -$H \boldsymbol c = \lambda_0 S \boldsymbol c$ where $\boldsymbol c_i = c_i$, $H_{ij} = \text{Tr}[M_i^\dagger H M_i \rho]$, and $S_{ij} = \text{Tr}[M_i^\dagger M_i \rho]$. The eigenvector $\boldsymbol c$ corresponding to the lowest $\lambda$ is the solution to the minimization problem. +As mentioned, the main idea is to expand around $\rho$ using the check operators, and then search this subspace for the state with least amount of error. The state with the least amount of energy is defined as the state in the subspace that minimizes $H = - \sum_i M_i$. Thus the full procedure can be formulated as follows, +$\min_{{c_i}} \mathrm{tr}[\bar P_c \rho \bar P_c^\dagger H ]$ subject to the constrain $\mathrm{tr}[\bar P_c \rho \bar P_c^\dagger] = 1$ with $\bar P_c = \sum c_i M_i$. -In practice, $H_{ij}$ and $S_{ij}$ can be measured on the quantum computer and the eigenvalue problem can be solved classically. Once $\boldsymbol c$ is obtained, we can construct $\bar P_c$ and use it to calculate any observable $A$ of interest using $\text{Tr}[ \bar P_c \rho \bar P_c^\dagger A ]$. +This minimization problem can be mapped to the following generalized eigenvalue problem: +$H \boldsymbol c = \lambda_0 S \boldsymbol c$ where $\boldsymbol c_i = c_i$, $H_{ij} = \mathrm{tr}[M_i^\dagger H M_i \rho]$, and $S_{ij} = \mathrm{tr}[M_i^\dagger M_i \rho]$. The eigenvector $\boldsymbol c$ corresponding to the lowest $\lambda$ is the solution to the minimization problem. + +In practice, $H_{ij}$ and $S_{ij}$ can be measured on the quantum computer and the eigenvalue problem can be solved classically. Once $\boldsymbol c$ is obtained, we can construct $\bar P_c$ and use it to calculate any observable $A$ of interest using $\mathrm{tr}[ \bar P_c \rho \bar P_c^\dagger A ]$. diff --git a/docs/source/guide/zne-2-use-case.md b/docs/source/guide/zne-2-use-case.md index 2d4c5c21df..b657ec8156 100644 --- a/docs/source/guide/zne-2-use-case.md +++ b/docs/source/guide/zne-2-use-case.md @@ -17,7 +17,7 @@ kernelspec: Zero noise extrapolation is one of the simplest error mitigation techniques and, in many practical situations, it can be applied with a relatively small sampling cost. The main advantage of ZNE is that the technique can be applied without a detailed knowledge of -the undelying noise model. Therefore it can be a good option in situations where +the underlying noise model. Therefore it can be a good option in situations where tomography is impractical. diff --git a/docs/source/refs.bib b/docs/source/refs.bib index 1280ac42f7..f7b6762353 100644 --- a/docs/source/refs.bib +++ b/docs/source/refs.bib @@ -731,6 +731,16 @@ @misc{Russo_2022_Testing year = {2022}, copyright = {arXiv.org perpetual, non-exclusive license}, } + +@misc{Russo_2024_LRE, + title={Quantum error mitigation by layerwise Richardson extrapolation}, + author={Vincent Russo and Andrea Mari}, + year={2024}, + eprint={2402.04000}, + archivePrefix={arXiv}, + primaryClass={quant-ph} +} + # Letter S @article{Sagastizabal_2019_PRA, diff --git a/docs/source/release.md b/docs/source/release.md index 49601a47e4..3bdec98c76 100644 --- a/docs/source/release.md +++ b/docs/source/release.md @@ -116,9 +116,8 @@ release Mitiq are: ```bash python -m pip install --upgrade pip -make install requirements -pip install setuptools wheel twine -python setup.py sdist bdist_wheel +pip install build twine +python -m build twine upload dist/* ``` diff --git a/mitiq/_about.py b/mitiq/_about.py index 41e1c52cf0..2d2afa4188 100644 --- a/mitiq/_about.py +++ b/mitiq/_about.py @@ -24,9 +24,9 @@ def about() -> None: pyquil_version = "Not installed" try: - from qiskit import __qiskit_version__ # pragma: no cover + from qiskit import __qiskit_version__ - qiskit_version = __qiskit_version__["qiskit"] # pragma: no cover + qiskit_version = __qiskit_version__["qiskit"] except ImportError: qiskit_version = "Not installed" diff --git a/mitiq/benchmarks/randomized_benchmarking.py b/mitiq/benchmarks/randomized_benchmarking.py index 8c5360c391..d0a42c15ca 100644 --- a/mitiq/benchmarks/randomized_benchmarking.py +++ b/mitiq/benchmarks/randomized_benchmarking.py @@ -16,7 +16,7 @@ import numpy as np from cirq.experiments.qubit_characterizations import ( _find_inv_matrix, - _gate_seq_to_mats, + _reduce_gate_seq, _single_qubit_cliffords, _two_qubit_clifford, _two_qubit_clifford_matrices, @@ -60,9 +60,6 @@ def generate_rb_circuits( rng = np.random.RandomState(seed) if n_qubits == 1: c1 = cliffords.c1_in_xy - cfd_mat_1q = np.array( - [_gate_seq_to_mats(gates) for gates in c1], dtype=np.complex64 - ) circuits = [] clifford_group_size = 24 for _ in range(trials): @@ -70,10 +67,7 @@ def generate_rb_circuits( gate_sequence = [ gate for gate_id in gate_ids for gate in c1[gate_id] ] - idx = _find_inv_matrix( - _gate_seq_to_mats(gate_sequence), cfd_mat_1q - ) - gate_sequence.extend(c1[idx]) + gate_sequence.append(_reduce_gate_seq(gate_sequence) ** -1) circuits.append( cirq.Circuit(gate(qubits[0]) for gate in gate_sequence) ) diff --git a/mitiq/executor/executor.py b/mitiq/executor/executor.py index 7ace20c78c..3dcef180ef 100644 --- a/mitiq/executor/executor.py +++ b/mitiq/executor/executor.py @@ -57,6 +57,13 @@ class Executor: """Tool for efficiently scheduling/executing quantum programs and storing the results. + + Args: + executor: A function which inputs a program and outputs a + ``mitiq.QuantumResult``, or inputs a sequence of programs and + outputs a sequence of ``mitiq.QuantumResult`` s. + max_batch_size: Maximum number of programs that can be sent in a + single batch (if the executor is batched). """ def __init__( @@ -64,15 +71,6 @@ def __init__( executor: Callable[[Union[QPROGRAM, Sequence[QPROGRAM]]], Any], max_batch_size: int = 75, ) -> None: - """Initializes an Executor. - - Args: - executor: A function which inputs a program and outputs a - ``mitiq.QuantumResult``, or inputs a sequence of programs and - outputs a sequence of ``mitiq.QuantumResult`` s. - max_batch_size: Maximum number of programs that can be sent in a - single batch (if the executor is batched). - """ self._executor = executor executor_annotation = inspect.getfullargspec(executor).annotations diff --git a/mitiq/interface/mitiq_qiskit/conversions.py b/mitiq/interface/mitiq_qiskit/conversions.py index 5d63afb5ef..72fb4c94fb 100644 --- a/mitiq/interface/mitiq_qiskit/conversions.py +++ b/mitiq/interface/mitiq_qiskit/conversions.py @@ -14,6 +14,7 @@ import numpy as np import qiskit from cirq.contrib.qasm_import import circuit_from_qasm +from cirq.contrib.qasm_import.exception import QasmException from qiskit import qasm2 from qiskit.transpiler import PassManager from qiskit.transpiler.layout import Layout @@ -248,7 +249,15 @@ def from_qiskit(circuit: qiskit.QuantumCircuit) -> cirq.Circuit: Returns: Mitiq circuit representation equivalent to the input Qiskit circuit. """ - return from_qasm(qasm2.dumps(circuit)) + try: + mitiq_circuit = from_qasm(qasm2.dumps(circuit)) + except QasmException: + # Try to decompose circuit before running + # This is necessary for converting qiskit circuits with + # custom packaged gates, e.g., QFT gates + circuit = circuit.decompose() + mitiq_circuit = from_qasm(qasm2.dumps(circuit)) + return mitiq_circuit def from_qasm(qasm: QASMType) -> cirq.Circuit: diff --git a/mitiq/interface/mitiq_qiskit/tests/test_conversions_qiskit.py b/mitiq/interface/mitiq_qiskit/tests/test_conversions_qiskit.py index c784d15e77..4ceb8d5d4d 100644 --- a/mitiq/interface/mitiq_qiskit/tests/test_conversions_qiskit.py +++ b/mitiq/interface/mitiq_qiskit/tests/test_conversions_qiskit.py @@ -203,6 +203,19 @@ def test_random_circuit_to_from_qasm(): ) +def test_convert_with_qft(): + """Tests converting a Qiskit circuit with a QFT to a Cirq circuit.""" + circuit = qiskit.QuantumCircuit(1) + circuit &= qiskit.circuit.library.QFT(1) + circuit.measure_all() + qft_cirq = from_qiskit(circuit) + qreg = cirq.LineQubit.range(1) + cirq_circuit = cirq.Circuit( + [cirq.ops.H.on(qreg[0]), cirq.ops.measure(qreg[0], key="meas")] + ) + assert _equal(cirq_circuit, qft_cirq) + + @pytest.mark.parametrize("as_qasm", (True, False)) def test_convert_with_barrier(as_qasm): """Tests converting a Qiskit circuit with a barrier to a Cirq circuit.""" diff --git a/mitiq/interface/mitiq_qiskit/transpiler.py b/mitiq/interface/mitiq_qiskit/transpiler.py index 32e72efd2a..1a966d291d 100644 --- a/mitiq/interface/mitiq_qiskit/transpiler.py +++ b/mitiq/interface/mitiq_qiskit/transpiler.py @@ -37,14 +37,12 @@ class ApplyMitiqLayout(TransformationPass): # type: ignore qubits by applying the Layout given in `property_set`. Requires either of passes to set/select Layout, e.g. `SetLayout`, `TrivialLayout`. Assumes the Layout has full physical qubits. + + Args: + new_qregs: The new quantum registers for the circuit. """ def __init__(self, new_qregs: List[QuantumRegister]) -> None: - """ApplyMitiqLayout constructor. - - Args: - new_qregs: The new quantum registers for the circuit. - """ super().__init__() self._new_qregs = new_qregs @@ -104,7 +102,6 @@ class ClearLayout(TransformationPass): # type: ignore """Clears the layout of the DAGCircuit""" def __init__(self) -> None: - """ClearLayout""" super().__init__() def run(self, dag: DAGCircuit) -> None: diff --git a/mitiq/lre/__init__.py b/mitiq/lre/__init__.py new file mode 100644 index 0000000000..1060e50c45 --- /dev/null +++ b/mitiq/lre/__init__.py @@ -0,0 +1,12 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Methods for scaling noise in circuits by layers and using multivariate extrapolation.""" + +from mitiq.lre.lre import execute_with_lre, mitigate_executor, lre_decorator + +from mitiq.lre import multivariate_scaling + +from mitiq.lre import inference diff --git a/mitiq/lre/inference/__init__.py b/mitiq/lre/inference/__init__.py new file mode 100644 index 0000000000..3411c080fd --- /dev/null +++ b/mitiq/lre/inference/__init__.py @@ -0,0 +1,3 @@ +from mitiq.lre.inference.multivariate_richardson import ( + multivariate_richardson_coefficients, +) diff --git a/mitiq/lre/inference/multivariate_richardson.py b/mitiq/lre/inference/multivariate_richardson.py new file mode 100644 index 0000000000..00e3f56707 --- /dev/null +++ b/mitiq/lre/inference/multivariate_richardson.py @@ -0,0 +1,194 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Functions for multivariate richardson extrapolation as defined in +:cite:`Russo_2024_LRE`. +""" + +import warnings +from itertools import product +from typing import Any, Optional + +import numpy as np +from cirq import Circuit +from numpy.typing import NDArray + +from mitiq.lre.multivariate_scaling.layerwise_folding import ( + _get_scale_factor_vectors, +) + + +def _full_monomial_basis_term_exponents( + num_layers: int, degree: int +) -> list[tuple[int, ...]]: + """Finds exponents of monomial terms required to create the sample matrix + as defined in Section IIB of :cite:`Russo_2024_LRE`. + + $Mj(λ_i, d)$ is the basis of monomial terms for $l$-layers in the input + circuit up to a specific degree $d$. The linear combination defines our + polynomial of interest. In general, the number of monomial terms for a + variable $l$ up to degree $d$ can be determined through the Stars and + Bars method. + + We assume the terms in the monomial basis are arranged in a graded + lexicographic order such that the terms with the highest total degree are + considered to be the largest and the remaining terms are arranged in + lexicographic order. + + For `degree=2, num_layers=2`, the monomial terms basis are + ${1, x_1, x_2, x_1**2, x_1x_2, x_2**2}$ i.e. the function returns the + exponents of x_1, x_2 as + `[(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)]`. + """ + exponents = { + exps + for exps in product(range(degree + 1), repeat=num_layers) + if sum(exps) <= degree + } + + return sorted(exponents, key=lambda term: (sum(term), term[::-1])) + + +def sample_matrix( + input_circuit: Circuit, + degree: int, + fold_multiplier: int, + num_chunks: Optional[int] = None, +) -> NDArray[Any]: + r""" + Defines the square sample matrix required for multivariate extrapolation as + defined in :cite:`Russo_2024_LRE`. + + The number of monomial terms should be equal to the + number of scale factor vectors such that the monomial terms define the rows + and the scale factor vectors define the columns. + + Args: + input_circuit: Circuit to be scaled. + degree: Degree of the multivariate polynomial. + fold_multiplier: Scaling gap required by unitary folding. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + + Returns: + Matrix of the evaluated monomial basis terms from the scale factor + vectors. + + Raises: + ValueError: + When the degree for the multinomial is not greater than or + equal to 1; when the fold multiplier to scale the circuit is + greater than/equal to 1; when the number of chunks for a + large circuit is 0 or when the number of chunks in a circuit is + greater than the number of layers in the input circuit. + + """ + if degree < 1: + raise ValueError( + "Multinomial degree must be greater than or equal to 1." + ) + if fold_multiplier < 1: + raise ValueError("Fold multiplier must be greater than or equal to 1.") + + scale_factor_vectors = _get_scale_factor_vectors( + input_circuit, degree, fold_multiplier, num_chunks + ) + num_layers = len(scale_factor_vectors[0]) + + # Evaluate the monomial terms using the values in the scale factor vectors + # and insert in the sample matrix + # each row is specific to each scale factor vector + # each column is a term in the monomial basis + variable_exp = _full_monomial_basis_term_exponents(num_layers, degree) + sample_matrix = np.empty((len(variable_exp), len(variable_exp))) + + for i, scale_factors in enumerate(scale_factor_vectors): + for j, exponent in enumerate(variable_exp): + evaluated_terms = [] + for base, exp in zip(scale_factors, exponent): + # raise scale factor value by the exponent dict value + evaluated_terms.append(base**exp) + sample_matrix[i, j] = np.prod(evaluated_terms) + + # verify the matrix is square + mat_row, mat_cols = sample_matrix.shape + assert mat_row == mat_cols + + return sample_matrix + + +def multivariate_richardson_coefficients( + input_circuit: Circuit, + degree: int, + fold_multiplier: int, + num_chunks: Optional[int] = None, +) -> list[float]: + r""" + Defines the function to find the linear combination coefficients from the + sample matrix as required for multivariate extrapolation (defined in + :cite:`Russo_2024_LRE`). + + We use the sample matrix to find the constants of linear combination + $c = (c_1, c_2, c_3, …, c_M)$ associated with a known vector of noisy + expectation values $z = (, , , ..., )^T$. + + The coefficients are found through the ratio of the determinants of $M_i$ + and the sample matrix. The new matrix $M_i$ is defined by replacing the ith + row of the sample matrix with $e_1 = (1, 0, 0,..., 0)$. + + + Args: + input_circuit: Circuit to be scaled. + degree: Degree of the multivariate polynomial. + fold_multiplier: Scaling gap required by unitary folding. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + + Returns: + List of the evaluated monomial basis terms using the scale factor + vectors. + """ + input_sample_matrix = sample_matrix( + input_circuit, degree, fold_multiplier, num_chunks + ) + num_layers = len( + _get_scale_factor_vectors( + input_circuit, degree, fold_multiplier, num_chunks + ) + ) + try: + det = np.linalg.det(input_sample_matrix) + except RuntimeWarning: # pragma: no cover + # taken from https://stackoverflow.com/a/19317237 + warnings.warn( # pragma: no cover + "To account for overflow error, required determinant of " + + "large sample matrix is calculated through " + + "`np.linalg.slogdet`." + ) + sign, logdet = np.linalg.slogdet( # pragma: no cover + input_sample_matrix + ) + det = sign * np.exp(logdet) # pragma: no cover + + if np.isinf(det): + raise ValueError( # pragma: no cover + "Determinant of sample matrix cannot be calculated as " + + "the matrix is too large. Consider chunking your" + + " input circuit. " + ) + assert det != 0.0 + coeff_list = [] + # replace a row of the sample matrix with [1, 0, 0, .., 0] + for i in range(num_layers): + sample_matrix_copy = input_sample_matrix.copy() + sample_matrix_copy[i] = np.array([[1] + [0] * (num_layers - 1)]) + coeff_list.append( + np.linalg.det(sample_matrix_copy) + / np.linalg.det(input_sample_matrix) + ) + + return coeff_list diff --git a/mitiq/lre/lre.py b/mitiq/lre/lre.py new file mode 100644 index 0000000000..e292e15d19 --- /dev/null +++ b/mitiq/lre/lre.py @@ -0,0 +1,172 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Extrapolation methods for Layerwise Richardson Extrapolation (LRE)""" + +from functools import wraps +from typing import Any, Callable, Optional, Union + +import numpy as np +from cirq import Circuit + +from mitiq import QPROGRAM +from mitiq.lre.inference import ( + multivariate_richardson_coefficients, +) +from mitiq.lre.multivariate_scaling import ( + multivariate_layer_scaling, +) +from mitiq.zne.scaling import fold_gates_at_random + + +def execute_with_lre( + input_circuit: Circuit, + executor: Callable[[Circuit], float], + degree: int, + fold_multiplier: int, + folding_method: Callable[ + [QPROGRAM, float], QPROGRAM + ] = fold_gates_at_random, # type: ignore [has-type] + num_chunks: Optional[int] = None, +) -> float: + r""" + Defines the executor required for Layerwise Richardson + Extrapolation as defined in :cite:`Russo_2024_LRE`. + + Note that this method only works for the multivariate extrapolation + methods. It does not allows a user to choose which layers in the input + circuit will be scaled. + + .. seealso:: + + If you would prefer to choose the layers for unitary + folding, use :func:`mitiq.zne.scaling.layer_scaling.get_layer_folding` + instead. + + Args: + input_circuit: Circuit to be scaled. + executor: Executes a circuit and returns a `float` + degree: Degree of the multivariate polynomial. + fold_multiplier: Scaling gap value required for unitary folding which + is used to generate the scale factor vectors. + folding_method: Unitary folding method. Default is + :func:`fold_gates_at_random`. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + + + Returns: + Error-mitigated expectation value + + """ + noise_scaled_circuits = multivariate_layer_scaling( + input_circuit, degree, fold_multiplier, num_chunks, folding_method + ) + + linear_combination_coeffs = multivariate_richardson_coefficients( + input_circuit, degree, fold_multiplier, num_chunks + ) + + # verify the linear combination coefficients and the calculated expectation + # values have the same length + if len(noise_scaled_circuits) != len( # pragma: no cover + linear_combination_coeffs + ): + raise AssertionError( + "The number of expectation values are not equal " + + "to the number of coefficients required for " + + "multivariate extrapolation." + ) + + lre_exp_values = [] + for scaled_circuit in noise_scaled_circuits: + circ_exp_val = executor(scaled_circuit) + lre_exp_values.append(circ_exp_val) + + return np.dot(lre_exp_values, linear_combination_coeffs) + + +def mitigate_executor( + executor: Callable[[Circuit], float], + degree: int, + fold_multiplier: int, + folding_method: Callable[ + [Union[Any], float], Union[Any] + ] = fold_gates_at_random, + num_chunks: Optional[int] = None, +) -> Callable[[Circuit], float]: + """Returns a modified version of the input `executor` which is + error-mitigated with layerwise richardson extrapolation (LRE). + + Args: + input_circuit: Circuit to be scaled. + executor: Executes a circuit and returns a `float` + degree: Degree of the multivariate polynomial. + fold_multiplier Scaling gap value required for unitary folding which + is used to generate the scale factor vectors. + folding_method: Unitary folding method. Default is + :func:`fold_gates_at_random`. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + + + Returns: + Error-mitigated version of the circuit executor. + """ + + @wraps(executor) + def new_executor(input_circuit: Circuit) -> float: + return execute_with_lre( + input_circuit, + executor, + degree, + fold_multiplier, + folding_method, + num_chunks, + ) + + return new_executor + + +def lre_decorator( + degree: int, + fold_multiplier: int, + folding_method: Callable[[Circuit, float], Circuit] = fold_gates_at_random, + num_chunks: Optional[int] = None, +) -> Callable[[Callable[[Circuit], float]], Callable[[Circuit], float]]: + """Decorator which adds an error-mitigation layer based on + layerwise richardson extrapolation (LRE). + + Args: + input_circuit: Circuit to be scaled. + executor: Executes a circuit and returns a `float` + degree: Degree of the multivariate polynomial. + fold_multiplier Scaling gap value required for unitary folding which + is used to generate the scale factor vectors. + folding_method: Unitary folding method. Default is + :func:`fold_gates_at_random`. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + + + Returns: + Error-mitigated decorator. + """ + + def decorator( + executor: Callable[[Circuit], float], + ) -> Callable[[Circuit], float]: + return mitigate_executor( + executor, + degree, + fold_multiplier, + folding_method, + num_chunks, + ) + + return decorator diff --git a/mitiq/lre/multivariate_scaling/__init__.py b/mitiq/lre/multivariate_scaling/__init__.py new file mode 100644 index 0000000000..816dfaa208 --- /dev/null +++ b/mitiq/lre/multivariate_scaling/__init__.py @@ -0,0 +1,6 @@ + +"""Methods for scaling noise in circuits by layers as required for LRE.""" + +from mitiq.lre.multivariate_scaling.layerwise_folding import ( + multivariate_layer_scaling, +) diff --git a/mitiq/lre/multivariate_scaling/layerwise_folding.py b/mitiq/lre/multivariate_scaling/layerwise_folding.py new file mode 100644 index 0000000000..9bd883fca2 --- /dev/null +++ b/mitiq/lre/multivariate_scaling/layerwise_folding.py @@ -0,0 +1,210 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Functions for layerwise folding of input circuits to allow for multivariate +extrapolation as defined in :cite:`Russo_2024_LRE`. +""" + +import itertools +from copy import deepcopy +from typing import Any, Callable, List, Optional, Tuple + +import numpy as np +from cirq import Circuit + +from mitiq import QPROGRAM +from mitiq.utils import _append_measurements, _pop_measurements +from mitiq.zne.scaling import fold_gates_at_random +from mitiq.zne.scaling.folding import _check_foldable + + +def _get_num_layers_without_measurements(input_circuit: Circuit) -> int: + """Checks if the circuit has non-terminal measurements and returns the + number of layers in the input circuit without the terminal measurements. + + Args: + input_circuit: Circuit of interest. + + Returns: + num_layers: the number of layers in the input circuit without the + terminal measurements. + + """ + + _check_foldable(input_circuit) + circuit = deepcopy(input_circuit) + _pop_measurements(circuit) + return len(circuit) + + +def _get_chunks( + input_circuit: Circuit, num_chunks: Optional[int] = None +) -> List[Circuit]: + """Splits a circuit into approximately equal chunks. + + Adapted from: + https://stackoverflow.com/questions/2130016/splitting-a-list-into-n-parts-of-approximately-equal-length + + Args: + input_circuit: Circuit of interest. + num_chunks: Number of desired approximately equal chunks, + * when num_chunks == num_layers, the original circuit is + returned. + * when num_chunks == 1, the entire circuit is chunked into 1 + layer. + Returns: + split_circuit: Circuit of interest split into approximately equal + chunks. + + Raises: + ValueError: + When the number of chunks for the input circuit is larger than + the number of layers in the input circuit. + + ValueError: + When the number of chunks is less than 1. + + """ + num_layers = _get_num_layers_without_measurements(input_circuit) + if num_chunks is None: + num_chunks = num_layers + + if num_chunks < 1: + raise ValueError( + "Number of chunks should be greater than or equal to 1." + ) + + if num_chunks > num_layers: + raise ValueError( + f"Number of chunks {num_chunks} cannot be greater than the number" + f" of layers {num_layers}." + ) + + k, m = divmod(num_layers, num_chunks) + return [ + input_circuit[i * k + min(i, m) : (i + 1) * k + min(i + 1, m)] + for i in range(num_chunks) + ] + + +def _get_scale_factor_vectors( + input_circuit: Circuit, + degree: int, + fold_multiplier: int, + num_chunks: Optional[int] = None, +) -> List[Tuple[Any, ...]]: + """Returns the patterned scale factor vectors required for multivariate + extrapolation. + + Args: + input_circuit: Circuit to be scaled. + degree: Degree of the multivariate polynomial. + fold_multiplier: Scaling gap required by unitary folding. + num_chunks: Number of desired approximately equal chunks. + + Returns: + scale_factor_vectors: A vector of scale factors where each + component in the vector corresponds to the layer in the input + circuit. + """ + + circuit_chunks = _get_chunks(input_circuit, num_chunks) + num_layers = len(circuit_chunks) + + # Find the exponents of all the monomial terms required for the folding + # pattern. + pattern_full = [] + for i in range(degree + 1): + for j in itertools.combinations_with_replacement(range(num_layers), i): + pattern = np.zeros(num_layers, dtype=int) + # Get the monomial terms in graded lexicographic order. + for index in j: + pattern[index] += 1 + # Use the fold multiplier on the folding pattern to determine which + # layers will be scaled. + pattern_full.append(tuple(fold_multiplier * pattern)) + + # Get the scale factor vectors. + # The layers are scaled as 2n+1 due to unitary folding. + return [ + tuple(2 * num_folds + 1 for num_folds in pattern) + for pattern in pattern_full + ] + + +def multivariate_layer_scaling( + input_circuit: Circuit, + degree: int, + fold_multiplier: int, + num_chunks: Optional[int] = None, + folding_method: Callable[ + [QPROGRAM, float], QPROGRAM + ] = fold_gates_at_random, +) -> List[Circuit]: + r""" + Defines the noise scaling function required for Layerwise Richardson + Extrapolation as defined in :cite:`Russo_2024_LRE`. + + Note that this method only works for the multivariate extrapolation + methods. It does not allows a user to choose which layers in the input + circuit will be scaled. + + .. seealso:: + + If you would prefer to choose the layers for unitary + folding, use :func:`mitiq.zne.scaling.layer_scaling.get_layer_folding` + instead. + + Args: + input_circuit: Circuit to be scaled. + degree: Degree of the multivariate polynomial. + fold_multiplier: Scaling gap required by unitary folding. + num_chunks: Number of desired approximately equal chunks. When the + number of chunks is the same as the layers in the input circuit, + the input circuit is unchanged. + folding_method: Unitary folding method. Default is + :func:`fold_gates_at_random`. + + Returns: + Multiple folded variations of the input circuit. + + Raises: + ValueError: + When the degree for the multinomial is not greater than or + equal to 1; when the fold multiplier to scale the circuit is + greater than/equal to 1; when the number of chunks for a + large circuit is 0 or when the number of chunks in a circuit is + greater than the number of layers in the input circuit. + + """ + if degree < 1: + raise ValueError( + "Multinomial degree must be greater than or equal to 1." + ) + if fold_multiplier < 1: + raise ValueError("Fold multiplier must be greater than or equal to 1.") + circuit_copy = deepcopy(input_circuit) + terminal_measurements = _pop_measurements(circuit_copy) + + scaling_pattern = _get_scale_factor_vectors( + circuit_copy, degree, fold_multiplier, num_chunks + ) + + chunks = _get_chunks(circuit_copy, num_chunks) + + multiple_folded_circuits = [] + for scale_factor_vector in scaling_pattern: + folded_circuit = Circuit() + for chunk, scale_factor in zip(chunks, scale_factor_vector): + if scale_factor == 1: + folded_circuit += chunk + else: + chunks_circ = Circuit(chunk) + folded_chunk_circ = folding_method(chunks_circ, scale_factor) + folded_circuit += folded_chunk_circ + _append_measurements(folded_circuit, terminal_measurements) + multiple_folded_circuits.append(folded_circuit) + + return multiple_folded_circuits diff --git a/mitiq/lre/tests/test_layerwise_folding.py b/mitiq/lre/tests/test_layerwise_folding.py new file mode 100644 index 0000000000..b1870450d8 --- /dev/null +++ b/mitiq/lre/tests/test_layerwise_folding.py @@ -0,0 +1,301 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Unit tests for scaling noise by unitary folding of layers in the input +circuit to allow for multivariate extrapolation.""" + +from copy import deepcopy + +import pytest +from cirq import Circuit, LineQubit, ops + +from mitiq.lre.multivariate_scaling.layerwise_folding import ( + _get_chunks, + _get_num_layers_without_measurements, + _get_scale_factor_vectors, + multivariate_layer_scaling, +) + +qreg1 = LineQubit.range(3) +test_circuit1 = Circuit( + [ops.H.on_each(*qreg1)], + [ops.CNOT.on(qreg1[0], qreg1[1])], + [ops.X.on(qreg1[2])], + [ops.TOFFOLI.on(*qreg1)], +) + +test_circuit1_with_measurements = deepcopy(test_circuit1) +test_circuit1_with_measurements.append(ops.measure_each(*qreg1)) + + +def test_multivariate_layerwise_scaling(): + """Checks if multiple scaled circuits are returned to fit the required + folding pattern for multivariate extrapolation.""" + multiple_scaled_circuits = multivariate_layer_scaling( + test_circuit1, 2, 2, 3 + ) + + assert len(multiple_scaled_circuits) == 10 + folding_pattern = [ + (1, 1, 1), + (5, 1, 1), + (1, 5, 1), + (1, 1, 5), + (9, 1, 1), + (5, 5, 1), + (5, 1, 5), + (1, 9, 1), + (1, 5, 5), + (1, 1, 9), + ] + + for i, scale_factor_vector in enumerate(folding_pattern): + scale_layer1, scale_layer2, scale_layer3 = scale_factor_vector + expected_circuit = Circuit( + [ops.H.on_each(*qreg1)] * scale_layer1, + [ops.CNOT.on(qreg1[0], qreg1[1]), ops.X.on(qreg1[2])] + * scale_layer2, + [ops.TOFFOLI.on(*qreg1)] * scale_layer3, + ) + assert expected_circuit == multiple_scaled_circuits[i] + + +@pytest.mark.parametrize( + "test_input, expected", + [(test_circuit1, 3), (test_circuit1_with_measurements, 3)], +) +def test_get_num_layers(test_input, expected): + """Verifies function works as expected.""" + calculated_num_layers = _get_num_layers_without_measurements(test_input) + + assert calculated_num_layers == expected + + +@pytest.mark.parametrize( + "test_input, test_chunks", + [ + (test_circuit1 + test_circuit1 + test_circuit1, 3), + (test_circuit1 + test_circuit1 + test_circuit1, 1), + (test_circuit1 + test_circuit1 + test_circuit1, 5), + ], +) +def test_get_num_chunks(test_input, test_chunks): + """Verifies the chunking function works as expected.""" + assert test_chunks == len(_get_chunks(test_input, test_chunks)) + + +def test_layers_with_chunking(): + """Checks the order of moments in the input circuit is unchanged with + chunking.""" + + test_circuit = test_circuit1 + test_circuit1 + test_circuit1 + calculated_circuit_chunks = _get_chunks(test_circuit, 4) + expected_chunks = [ + test_circuit[0:3], + test_circuit[3:5], + test_circuit[5:7], + test_circuit[7:], + ] + assert calculated_circuit_chunks == expected_chunks + + +@pytest.mark.parametrize( + "test_input, degree, test_fold_multiplier, expected_scale_factor_vectors", + [ + (test_circuit1, 1, 1, [(1, 1, 1), (3, 1, 1), (1, 3, 1), (1, 1, 3)]), + ( + test_circuit1, + 2, + 1, + [ + (1, 1, 1), + (3, 1, 1), + (1, 3, 1), + (1, 1, 3), + (5, 1, 1), + (3, 3, 1), + (3, 1, 3), + (1, 5, 1), + (1, 3, 3), + (1, 1, 5), + ], + ), + ( + test_circuit1, + 2, + 2, + [ + (1, 1, 1), + (5, 1, 1), + (1, 5, 1), + (1, 1, 5), + (9, 1, 1), + (5, 5, 1), + (5, 1, 5), + (1, 9, 1), + (1, 5, 5), + (1, 1, 9), + ], + ), + ( + test_circuit1, + 2, + 3, + [ + (1, 1, 1), + (7, 1, 1), + (1, 7, 1), + (1, 1, 7), + (13, 1, 1), + (7, 7, 1), + (7, 1, 7), + (1, 13, 1), + (1, 7, 7), + (1, 1, 13), + ], + ), + ( + test_circuit1_with_measurements, + 1, + 1, + [(1, 1, 1), (3, 1, 1), (1, 3, 1), (1, 1, 3)], + ), + ( + test_circuit1_with_measurements, + 2, + 1, + [ + (1, 1, 1), + (3, 1, 1), + (1, 3, 1), + (1, 1, 3), + (5, 1, 1), + (3, 3, 1), + (3, 1, 3), + (1, 5, 1), + (1, 3, 3), + (1, 1, 5), + ], + ), + ( + test_circuit1_with_measurements, + 2, + 2, + [ + (1, 1, 1), + (5, 1, 1), + (1, 5, 1), + (1, 1, 5), + (9, 1, 1), + (5, 5, 1), + (5, 1, 5), + (1, 9, 1), + (1, 5, 5), + (1, 1, 9), + ], + ), + ( + test_circuit1_with_measurements, + 2, + 3, + [ + (1, 1, 1), + (7, 1, 1), + (1, 7, 1), + (1, 1, 7), + (13, 1, 1), + (7, 7, 1), + (7, 1, 7), + (1, 13, 1), + (1, 7, 7), + (1, 1, 13), + ], + ), + ], +) +def test_get_scale_factor_vectors_no_chunking( + test_input, degree, test_fold_multiplier, expected_scale_factor_vectors +): + """Verifies vectors of scale factors are calculated accurately.""" + calculated_scale_factor_vectors = _get_scale_factor_vectors( + test_input, degree, test_fold_multiplier + ) + + assert calculated_scale_factor_vectors == expected_scale_factor_vectors + + +@pytest.mark.parametrize( + "test_input, degree, test_fold_multiplier, test_chunks, expected_size", + [ + (test_circuit1, 1, 1, 2, 3), + (test_circuit1, 2, 1, 3, 10), + (test_circuit1, 2, 3, 2, 6), + ], +) +def test_get_scale_factor_vectors_with_chunking( + test_input, degree, test_fold_multiplier, test_chunks, expected_size +): + """Verifies vectors of scale factors are calculated accurately.""" + calculated_scale_factor_vectors = _get_scale_factor_vectors( + test_input, degree, test_fold_multiplier, test_chunks + ) + + assert len(calculated_scale_factor_vectors) == expected_size + + +@pytest.mark.parametrize( + "test_input, num_chunks, error_msg", + [ + ( + test_circuit1, + 0, + "Number of chunks should be greater than or equal to 1.", + ), + ( + test_circuit1, + 5, + "Number of chunks 5 cannot be greater than the number of layers" + " 3.", + ), + ( + test_circuit1, + -1, + "Number of chunks should be greater than or equal to 1.", + ), + ], +) +def test_invalid_num_chunks(test_input, num_chunks, error_msg): + """Ensures that the number of intended chunks in the input circuit raises + an error for an invalid value.""" + with pytest.raises(ValueError, match=error_msg): + _get_scale_factor_vectors(test_input, 2, 2, num_chunks) + + +@pytest.mark.parametrize( + "test_input, test_degree, test_fold_multiplier, error_msg", + [ + ( + test_circuit1, + 0, + 1, + "Multinomial degree must be greater than or equal to 1.", + ), + ( + test_circuit1, + 1, + 0, + "Fold multiplier must be greater than or equal to 1.", + ), + ], +) +def test_invalid_degree_fold_multiplier( + test_input, test_degree, test_fold_multiplier, error_msg +): + """Ensures that the args for the main noise scaling function raise + an error for an invalid value.""" + with pytest.raises(ValueError, match=error_msg): + multivariate_layer_scaling( + test_input, test_degree, test_fold_multiplier + ) diff --git a/mitiq/lre/tests/test_lre.py b/mitiq/lre/tests/test_lre.py new file mode 100644 index 0000000000..9afb9715d7 --- /dev/null +++ b/mitiq/lre/tests/test_lre.py @@ -0,0 +1,115 @@ +"""Unit tests for the LRE extrapolation methods.""" + +import re + +import pytest +from cirq import DensityMatrixSimulator, depolarize + +from mitiq import benchmarks +from mitiq.lre import execute_with_lre, lre_decorator, mitigate_executor +from mitiq.zne.scaling import fold_all, fold_global + +# default circuit for all unit tests +test_cirq = benchmarks.generate_rb_circuits( + n_qubits=1, + num_cliffords=2, +)[0] + + +# default execute function for all unit tests +def execute(circuit, noise_level=0.025): + """Default executor for all unit tests.""" + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix + return rho[0, 0].real + + +noisy_val = execute(test_cirq) +ideal_val = execute(test_cirq, noise_level=0) + + +@pytest.mark.parametrize("degree, fold_multiplier", [(2, 2), (2, 3), (3, 4)]) +def test_lre_exp_value(degree, fold_multiplier): + """Verify LRE executors work as expected.""" + lre_exp_val = execute_with_lre( + test_cirq, + execute, + degree=degree, + fold_multiplier=fold_multiplier, + ) + assert abs(lre_exp_val - ideal_val) <= abs(noisy_val - ideal_val) + + +@pytest.mark.parametrize("degree, fold_multiplier", [(2, 2), (2, 3), (3, 4)]) +def test_lre_exp_value_decorator(degree, fold_multiplier): + """Verify LRE mitigated executor work as expected.""" + mitigated_executor = mitigate_executor( + execute, degree=2, fold_multiplier=2 + ) + exp_val_from_mitigate_executor = mitigated_executor(test_cirq) + assert abs(exp_val_from_mitigate_executor - ideal_val) <= abs( + noisy_val - ideal_val + ) + + +def test_lre_decorator(): + """Verify LRE decorators work as expected.""" + + @lre_decorator(degree=2, fold_multiplier=2) + def execute(circuit, noise_level=0.025): + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = ( + DensityMatrixSimulator() + .simulate(noisy_circuit) + .final_density_matrix + ) + return rho[0, 0].real + + assert abs(execute(test_cirq) - ideal_val) <= abs(noisy_val - ideal_val) + + +def test_lre_decorator_raised_error(): + """Verify an error is raised when the required parameters for the decorator + are not specified.""" + with pytest.raises(TypeError, match=re.escape("lre_decorator() missing")): + + @lre_decorator() + def execute(circuit, noise_level=0.025): + noisy_circuit = circuit.with_noise(depolarize(p=noise_level)) + rho = ( + DensityMatrixSimulator() + .simulate(noisy_circuit) + .final_density_matrix + ) + return rho[0, 0].real + + assert abs(execute(test_cirq) - ideal_val) <= abs( + noisy_val - ideal_val + ) + + +def test_lre_executor_with_chunking(): + """Verify the executor works as expected for chunking a large circuit into + a smaller circuit.""" + # define a larger circuit + test_cirq = benchmarks.generate_rb_circuits(n_qubits=1, num_cliffords=12)[ + 0 + ] + lre_exp_val = execute_with_lre( + test_cirq, execute, degree=2, fold_multiplier=2, num_chunks=14 + ) + assert abs(lre_exp_val - ideal_val) <= abs(noisy_val - ideal_val) + + +@pytest.mark.parametrize("input_method", [(fold_global), (fold_all)]) +def test_lre_executor_with_different_folding_methods(input_method): + """Verify the executor works as expected for using non-default unitary + folding methods.""" + lre_exp_val = execute_with_lre( + test_cirq, + execute, + degree=2, + fold_multiplier=2, + folding_method=input_method, + ) + assert abs(lre_exp_val - ideal_val) <= abs(noisy_val - ideal_val) diff --git a/mitiq/lre/tests/test_multivariate_richardson.py b/mitiq/lre/tests/test_multivariate_richardson.py new file mode 100644 index 0000000000..c2b5f41729 --- /dev/null +++ b/mitiq/lre/tests/test_multivariate_richardson.py @@ -0,0 +1,234 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Unit tests for multivariate extrapolation inference functions.""" + +import numpy as np +import pytest +from cirq import Circuit, LineQubit, ops + +from mitiq.lre.inference.multivariate_richardson import ( + _full_monomial_basis_term_exponents, + multivariate_richardson_coefficients, + sample_matrix, +) +from mitiq.lre.multivariate_scaling.layerwise_folding import ( + multivariate_layer_scaling, +) + +qreg1 = LineQubit.range(3) +test_circuit1 = Circuit( + [ops.H.on_each(*qreg1)], + [ops.CNOT.on(qreg1[0], qreg1[1])], +) +test_circuit2 = Circuit( + [ops.H.on_each(*qreg1)], + [ops.CNOT.on(qreg1[0], qreg1[1])], + [ops.X.on(qreg1[2])], + [ops.TOFFOLI.on(*qreg1)], +) + + +@pytest.mark.parametrize( + "test_num_layers, test_degree, expected", + [ + (1, 1, [(0,), (1,)]), + ( + 2, + 2, + [(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)], + ), + ( + 3, + 2, + [ + (0, 0, 0), + (1, 0, 0), + (0, 1, 0), + (0, 0, 1), + (2, 0, 0), + (1, 1, 0), + (0, 2, 0), + (1, 0, 1), + (0, 1, 1), + (0, 0, 2), + ], + ), + ], +) +def test_basis_exp(test_num_layers, test_degree, expected): + assert ( + _full_monomial_basis_term_exponents(test_num_layers, test_degree) + == expected + ) + + +@pytest.mark.parametrize( + "test_num_layers, test_degree", + [(1, 1), (2, 2), (3, 2), (10, 4)], + # TO DO: Note need to add (100, 2) here. + # This makes the unit test very slow. +) +def test_basis_exp_len(test_num_layers, test_degree): + calc_dict = _full_monomial_basis_term_exponents( + test_num_layers, test_degree + ) + for i in calc_dict: + assert len(i) == test_num_layers + + +@pytest.mark.parametrize( + "test_circ, test_degree, expected_matrix", + [ + ( + test_circuit1, + 2, + np.array( + [ + [1.0, 1.0, 1.0, 1.0, 1.0, 1.0], + [1.0, 3.0, 1.0, 9.0, 3.0, 1.0], + [1.0, 1.0, 3.0, 1.0, 3.0, 9.0], + [1.0, 5.0, 1.0, 25.0, 5.0, 1.0], + [1.0, 3.0, 3.0, 9.0, 9.0, 9.0], + [1.0, 1.0, 5.0, 1.0, 5.0, 25.0], + ] + ), + ), + ( + test_circuit2, + 2, + np.array( + [ + [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], + [1.0, 3.0, 1.0, 1.0, 9.0, 3.0, 1.0, 3.0, 1.0, 1.0], + [1.0, 1.0, 3.0, 1.0, 1.0, 3.0, 9.0, 1.0, 3.0, 1.0], + [1.0, 1.0, 1.0, 3.0, 1.0, 1.0, 1.0, 3.0, 3.0, 9.0], + [1.0, 5.0, 1.0, 1.0, 25.0, 5.0, 1.0, 5.0, 1.0, 1.0], + [1.0, 3.0, 3.0, 1.0, 9.0, 9.0, 9.0, 3.0, 3.0, 1.0], + [1.0, 3.0, 1.0, 3.0, 9.0, 3.0, 1.0, 9.0, 3.0, 9.0], + [1.0, 1.0, 5.0, 1.0, 1.0, 5.0, 25.0, 1.0, 5.0, 1.0], + [1.0, 1.0, 3.0, 3.0, 1.0, 3.0, 9.0, 3.0, 9.0, 9.0], + [1.0, 1.0, 1.0, 5.0, 1.0, 1.0, 1.0, 5.0, 5.0, 25.0], + ] + ), + ), + ], +) +def test_sample_matrix(test_circ, test_degree, expected_matrix): + assert np.allclose( + expected_matrix, sample_matrix(test_circ, test_degree, 1), atol=1e-3 + ) + + +@pytest.mark.parametrize( + "test_circ, test_degree, test_fold_multiplier, expected_matrix", + [ + ( + test_circuit1, + 2, + 3, + [ + 1.5555555555555578, + -0.38888888888888934, + -0.38888888888888934, + 0.09722222222222215, + 0.027777777777777804, + 0.09722222222222232, + ], + ), + ( + test_circuit2, + 2, + 2, + [ + 2.4062499999999956, + -0.6874999999999987, + -0.6874999999999987, + -0.6874999999999987, + 0.15624999999999956, + 0.06249999999999997, + 0.06249999999999997, + 0.15624999999999956, + 0.06249999999999997, + 0.15624999999999956, + ], + ), + ], +) +def test_coeffs(test_circ, test_degree, test_fold_multiplier, expected_matrix): + assert np.allclose( + expected_matrix, + multivariate_richardson_coefficients( + test_circ, test_degree, test_fold_multiplier + ), + atol=1e-3, + ) + + assert np.isclose( + sum( + multivariate_richardson_coefficients( + test_circ, test_degree, test_fold_multiplier + ) + ), + 1.0, + ) + + +@pytest.mark.parametrize( + "test_input, test_degree, test_fold_multiplier, error_msg", + [ + ( + test_circuit1, + 0, + 1, + "Multinomial degree must be greater than or equal to 1.", + ), + ( + test_circuit1, + 1, + 0, + "Fold multiplier must be greater than or equal to 1.", + ), + ], +) +def test_invalid_degree_fold_multiplier_sample_matrix( + test_input, test_degree, test_fold_multiplier, error_msg +): + """Ensures that the args for the sample matrix + an error for an invalid value.""" + with pytest.raises(ValueError, match=error_msg): + sample_matrix(test_input, test_degree, test_fold_multiplier) + + +def test_lre_inference_with_chunking(): + """Verify the dimension of a chunked sample matrix for some input circuit + is smaller than the non-chunked sample matrix for the same input circuit. + """ + circ = test_circuit1 * 7 + chunked_sample_matrix_dim = sample_matrix(circ, 2, 2, num_chunks=4).shape + non_chunked_sample_matrix_dim = sample_matrix(circ, 2, 2).shape + assert chunked_sample_matrix_dim[0] < non_chunked_sample_matrix_dim[0] + + +def test_sample_matrix_numerical_stability(): + """Verify sample matrix function works for very large circuits.""" + large_circuit = Circuit([ops.H.on(LineQubit(i)) for i in range(10000)]) + matrix = sample_matrix(large_circuit, 5, 10000) + assert np.isfinite(matrix).all() + assert not np.isnan(matrix).any() + + +@pytest.mark.parametrize("num_chunks", [2, 3]) +def test_eval(num_chunks): + """Verify the number of calculated linear combination coefficients matches + to the number of scaled chunked circuits.""" + coeffs = multivariate_richardson_coefficients( + 7 * test_circuit2, 2, 2, num_chunks + ) + multiple_scaled_circuits = multivariate_layer_scaling( + 7 * test_circuit2, 2, 2, num_chunks + ) + assert len(coeffs) == len(multiple_scaled_circuits) + assert np.isclose(sum(coeffs), 1.0) diff --git a/mitiq/observable/observable.py b/mitiq/observable/observable.py index 09661e29ab..ce7d978bc5 100644 --- a/mitiq/observable/observable.py +++ b/mitiq/observable/observable.py @@ -20,15 +20,11 @@ class Observable: """A quantum observable typically used to compute its mitigated expectation value. + Args: + paulis: PauliStrings used to define the observable. """ def __init__(self, *paulis: PauliString) -> None: - """Initializes an `Observable` with :class:`.PauliString` objects. - - Args: - paulis: PauliStrings used to define the observable. - - """ self._paulis = _combine_duplicate_pauli_strings(paulis) self._groups: List[PauliStringCollection] self._ngroups: int diff --git a/mitiq/observable/pauli.py b/mitiq/observable/pauli.py index 56d4e3a960..bf68af671a 100644 --- a/mitiq/observable/pauli.py +++ b/mitiq/observable/pauli.py @@ -18,9 +18,21 @@ class PauliString: - """A `PauliString` is a (tensor) product of single-qubit Pauli gates I, X, - Y, and Z, with a leading (real or complex) coefficient. `PauliString`s can - be measured in any `mitiq.QPROGRAM`. + """A ``PauliString`` is a (tensor) product of single-qubit Pauli gates + :math:`I, X, Y`, and :math:`Z`, with a leading (real or complex) + coefficient. ``PauliString`` objects can be measured in any + ``mitiq.QPROGRAM``. + + Args: + spec: String specifier of the PauliString. Should only contain + characters 'I', 'X', 'Y', and 'Z'. + coeff: Coefficient of the PauliString. + support: Qubits the ``spec`` acts on, if provided. + + Examples: + >>> PauliString(spec="IXY") # X(1)*Y(2) + >>> PauliString(spec="ZZ", coeff=-0.5) # -0.5*Z(0)*Z(1) + >>> PauliString(spec="XZ", support=(10, 17)) # X(10)*Z(17) """ _string_to_gate_map = {"I": cirq.I, "X": cirq.X, "Y": cirq.Y, "Z": cirq.Z} @@ -31,19 +43,6 @@ def __init__( coeff: complex = 1.0, support: Optional[Sequence[int]] = None, ) -> None: - """Initialize a PauliString. - - Args: - spec: String specifier of the PauliString. Should only contain - characters 'I', 'X', 'Y', and 'Z'. - coeff: Coefficient of the PauliString. - support: Qubits the ``spec`` acts on, if provided. - - Examples: - >>> PauliString(spec="IXY") # X(1)*Y(2) - >>> PauliString(spec="ZZ", coeff=-0.5) # -0.5*Z(0)*Z(1) - >>> PauliString(spec="XZ", support=(10, 17)) # X(10)*Z(17) - """ if not set(spec).issubset(set(self._string_to_gate_map.keys())): raise ValueError( f"One or more invalid characters in spec {spec}. Valid " @@ -181,34 +180,32 @@ def __repr__(self) -> str: class PauliStringCollection: """A collection of PauliStrings that qubit-wise commute and so can be measured with a single circuit. + + Args: + paulis: PauliStrings to add to the collection. + check_precondition: If True, raises an error if some of the + ``PauliString`` objects do not qubit-wise commute. + + Example: + >>> pcol = PauliStringCollection( + >>> PauliString(spec="X"), + >>> PauliString(spec="IZ", coeff=-2.2) + >>> ) + >>> print(pcol) # X(0) + (-2.2+0j)*Z(1) + >>> print(pcol.support()) # {0, 1} + >>> + >>> # XZ qubit-wise commutes with X(0) and Z(1), so can be added. + >>> print(pcol.can_add(PauliString(spec="XZ"))) # True. + >>> pcol.add(PauliString(spec="XZ")) + >>> print(pcol) # X(0) + (-2.2+0j)*Z(1) + X(0)*Z(1) + >>> + >>> # Z(0) doesn't qubit-wise commute with X(0), so can't be added. + >>> print(pcol.can_add(PauliString(spec="Z"))) # False. """ def __init__( self, *paulis: PauliString, check_precondition: bool = True ) -> None: - """Initializes a `PauliStringCollection`. - - Args: - paulis: PauliStrings to add to the collection. - check_precondition: If True, raises an error if some of the - `PauliString`s do not qubit-wise commute. - - Example: - >>> pcol = PauliStringCollection( - >>> PauliString(spec="X"), - >>> PauliString(spec="IZ", coeff=-2.2) - >>> ) - >>> print(pcol) # X(0) + (-2.2+0j)*Z(1) - >>> print(pcol.support()) # {0, 1} - >>> - >>> # XZ qubit-wise commutes with X(0) and Z(1), so can be added. - >>> print(pcol.can_add(PauliString(spec="XZ"))) # True. - >>> pcol.add(PauliString(spec="XZ")) - >>> print(pcol) # X(0) + (-2.2+0j)*Z(1) + X(0)*Z(1) - >>> - >>> # Z(0) doesn't qubit-wise commute with X(0), so can't be added. - >>> print(pcol.can_add(PauliString(spec="Z"))) # False. - """ self._paulis_by_weight: Dict[int, TCounter[PauliString]] = dict() self.add(*paulis, check_precondition=check_precondition) diff --git a/mitiq/pea/pea.py b/mitiq/pea/pea.py new file mode 100644 index 0000000000..e819255201 --- /dev/null +++ b/mitiq/pea/pea.py @@ -0,0 +1 @@ +### Placeholder for PEA technique diff --git a/mitiq/pec/representations/optimal.py b/mitiq/pec/representations/optimal.py index e791887268..42ad39f7e3 100644 --- a/mitiq/pec/representations/optimal.py +++ b/mitiq/pec/representations/optimal.py @@ -146,7 +146,7 @@ def find_optimal_representation( " which are initialized with a numerical superoperator matrix." ) else: - raise err # pragma no cover + raise err # pragma: no cover # Run numerical optimization problem quasi_prob_dist = minimize_one_norm( diff --git a/mitiq/pec/types/types.py b/mitiq/pec/types/types.py index 515b323a6a..8325aa33c2 100644 --- a/mitiq/pec/types/types.py +++ b/mitiq/pec/types/types.py @@ -24,7 +24,18 @@ class NoisyOperation: """An operation (or sequence of operations) which a noisy quantum computer - can actually implement.p + can actually implement. + + Args: + circuit: A short circuit which, when executed on a given noisy + quantum computer, generates a noisy channel. It typically + contains a single-gate or a short sequence of gates. + channel_matrix: Superoperator representation of the noisy channel + which is generated when executing the input ``circuit`` on the + noisy quantum computer. + + Raises: + TypeError: If ``ideal`` is not a ``QPROGRAM``. """ def __init__( @@ -32,19 +43,6 @@ def __init__( circuit: QPROGRAM, channel_matrix: Optional[npt.NDArray[np.complex64]] = None, ) -> None: - """Initializes a NoisyOperation. - - Args: - circuit: A short circuit which, when executed on a given noisy - quantum computer, generates a noisy channel. It typically - contains a single-gate or a short sequence of gates. - channel_matrix: Superoperator representation of the noisy channel - which is generated when executing the input ``circuit`` on the - noisy quantum computer. - - Raises: - TypeError: If ``ideal`` is not a ``QPROGRAM``. - """ self._native_circuit = deepcopy(circuit) try: @@ -135,6 +133,20 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class OperationRepresentation: """A decomposition (basis expansion) of an operation or sequence of operations in a basis of noisy, implementable operations. + + Args: + ideal: The ideal operation desired to be implemented. + basis_expansion: Representation of the ideal operation in a basis + of ``NoisyOperation`` objects. + is_qubit_dependent: If True, the representation + corresponds to the operation on the specific qubits defined in + ``ideal``. If False, the representation is valid for the same + gate even if acting on different qubits from those specified in + ``ideal``. + + Raises: + TypeError: If all keys of ``basis_expansion`` are not instances of + ``NoisyOperation`` objects. """ def __init__( @@ -144,22 +156,6 @@ def __init__( coeffs: List[float], is_qubit_dependent: bool = True, ) -> None: - """Initializes an OperationRepresentation. - - Args: - ideal: The ideal operation desired to be implemented. - basis_expansion: Representation of the ideal operation in a basis - of `NoisyOperation`s. - is_qubit_dependent: If True, the representation - corresponds to the operation on the specific qubits defined in - `ideal`. If False, the representation is valid for the same - gate even if acting on different qubits from those specified in - `ideal`. - - Raises: - TypeError: If all keys of `basis_expansion` are not instances of - `NoisyOperation`s. - """ if not all(isinstance(o, NoisyOperation) for o in noisy_operations): raise TypeError( "All elements of `noisy_operations` must be " diff --git a/mitiq/pt/__init__.py b/mitiq/pt/__init__.py index e735c5ec8e..8edbfba609 100644 --- a/mitiq/pt/__init__.py +++ b/mitiq/pt/__init__.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. from mitiq.pt.pt import ( - pauli_twirl_circuit, + generate_pauli_twirl_variants, twirl_CNOT_gates, twirl_CZ_gates, ) diff --git a/mitiq/pt/pt.py b/mitiq/pt/pt.py index a76598b559..264a4f9993 100644 --- a/mitiq/pt/pt.py +++ b/mitiq/pt/pt.py @@ -4,9 +4,13 @@ # LICENSE file in the root directory of this source tree. import random -from typing import List +from functools import singledispatch +from typing import Callable, List, Optional import cirq +import pennylane as qml +from cirq import Circuit as _Circuit +from pennylane.tape import QuantumTape from mitiq import QPROGRAM from mitiq.interface import accept_qprogram_and_validate @@ -49,10 +53,24 @@ (cirq.Z, cirq.Z, cirq.Z, cirq.Z), ] +CIRQ_NOISE_FUNCTION = Callable[[float], cirq.Gate] -def pauli_twirl_circuit( +CIRQ_NOISE_OP: dict[str, CIRQ_NOISE_FUNCTION] = { + "bit-flip": cirq.bit_flip, + "depolarize": cirq.depolarize, +} + +PENNYLANE_NOISE_OP = { + "bit-flip": qml.BitFlip, + "depolarize": qml.DepolarizingChannel, +} + + +def generate_pauli_twirl_variants( circuit: QPROGRAM, num_circuits: int = 10, + noise_name: Optional[str] = None, + **kwargs: float, ) -> List[QPROGRAM]: r"""Return the Pauli twirled versions of the input circuit. @@ -61,20 +79,89 @@ def pauli_twirl_circuit( :cite:`Saki_2023_arxiv`. Args: - circuit: The input circuit to execute with twirling. - num_circuits: Number of circuits to be twirled, and averaged. + circuit: The input circuit on which twirling is applied. + num_circuits: Number of twirled variants of the circuits. + noise_name: Name of the noisy operator acting on CNOT and CZ gates. + This is useful if the user requires a noisy circuit after twirling. + Values allowed: ["bit-flip", "depolarize"] Returns: - The expectation value estimated with Pauli twirling. + A list of `num_circuits` twirled versions of `circuit` """ CNOT_twirled_circuits = twirl_CNOT_gates(circuit, num_circuits) twirled_circuits = [ twirl_CZ_gates(c, num_circuits=1)[0] for c in CNOT_twirled_circuits ] + if noise_name is not None: + twirled_circuits = [ + add_noise_to_two_qubit_gates(circuit, noise_name, **kwargs) + for circuit in twirled_circuits + ] + return twirled_circuits +def add_noise_to_two_qubit_gates( + circuit: QPROGRAM, noise_name: str, **kwargs: float +) -> QPROGRAM: + """Add noise to CNOT and CZ gates on pre-twirled circuits. + + Args: + circuit: Pre-twirled circuit + noise_name: name of noise operator to apply after CNOT and CZ gates + """ + # here we will validate if noise_op and kwargs are valid + return _add_noise_to_two_qubit_gates(circuit, noise_name, **kwargs) + + +@singledispatch +def _add_noise_to_two_qubit_gates( + circuit: QPROGRAM, noise_name: str, **kwargs: float +) -> QPROGRAM: + raise NotImplementedError( + f"Cannot add noise to Circuit of type {type(circuit)}." + ) + + +@_add_noise_to_two_qubit_gates.register +def _cirq(circuit: _Circuit, noise_name: str, **kwargs: float) -> _Circuit: + noise_function = CIRQ_NOISE_OP[noise_name] + noise_op = noise_function(**kwargs) # type: ignore + + noisy_gates = [cirq.ops.CNOT, cirq.ops.CZ] + + noisy_circuit = cirq.Circuit() + for moment in circuit: + layer = cirq.Circuit() + for op in moment: + layer.append(op) + if op.gate in noisy_gates: + layer.append(noise_op.on_each(op.qubits)) + noisy_circuit += layer + return noisy_circuit + + +@_add_noise_to_two_qubit_gates.register +def _pennylane( + circuit: QuantumTape, noise_name: str, **kwargs: float +) -> QuantumTape: + new_ops = [] + noise_function = PENNYLANE_NOISE_OP[noise_name] + + noisy_gates = ["CNOT", "CZ"] + for op in circuit: + new_ops.append(op) + if op.name in noisy_gates: + for wire in op.wires: + noise_op = noise_function(**kwargs, wires=wire) + new_ops.append(noise_op) + + return QuantumTape( + ops=new_ops, measurements=circuit.measurements, shots=circuit.shots + ) + + def twirl_CNOT_gates(circuit: QPROGRAM, num_circuits: int) -> List[QPROGRAM]: """Generate a list of circuits using Pauli twirling on CNOT gates. diff --git a/mitiq/pt/tests/test_pt.py b/mitiq/pt/tests/test_pt.py index e707d3b65b..3c6cb3860e 100644 --- a/mitiq/pt/tests/test_pt.py +++ b/mitiq/pt/tests/test_pt.py @@ -8,15 +8,19 @@ import cirq import networkx as nx import numpy as np +import pennylane as qml import pytest import qiskit +from pennylane.tape import QuantumTape from mitiq.benchmarks import generate_mirror_circuit from mitiq.interface.mitiq_cirq import compute_density_matrix from mitiq.pt.pt import ( + CIRQ_NOISE_OP, + PENNYLANE_NOISE_OP, CNOT_twirling_gates, CZ_twirling_gates, - pauli_twirl_circuit, + generate_pauli_twirl_variants, twirl_CNOT_gates, twirl_CZ_gates, ) @@ -118,7 +122,7 @@ def test_twirl_CNOT_increases_layer_count(): assert num_gates_after == num_gates_before -def test_pauli_twirl_circuit(): +def test_generate_pauli_twirl_variants(): num_qubits = 3 num_layers = 20 circuit, _ = generate_mirror_circuit( @@ -127,12 +131,13 @@ def test_pauli_twirl_circuit(): connectivity_graph=nx.complete_graph(num_qubits), ) num_circuits = 10 - twirled_output = pauli_twirl_circuit(circuit, num_circuits) + twirled_output = generate_pauli_twirl_variants(circuit, num_circuits) assert len(twirled_output) == num_circuits @pytest.mark.parametrize( - "twirl_func", [pauli_twirl_circuit, twirl_CNOT_gates, twirl_CZ_gates] + "twirl_func", + [generate_pauli_twirl_variants, twirl_CNOT_gates, twirl_CZ_gates], ) def test_no_CNOT_CZ_circuit(twirl_func): num_qubits = 2 @@ -144,3 +149,43 @@ def test_no_CNOT_CZ_circuit(twirl_func): for i in range(5): assert _equal(circuit, twirled_output[i]) + + +@pytest.mark.parametrize("noise_name", ["bit-flip", "depolarize"]) +def test_noisy_cirq(noise_name): + p = 0.01 + a, b = cirq.LineQubit.range(2) + circuit = cirq.Circuit(cirq.H.on(a), cirq.CNOT.on(a, b), cirq.CZ.on(a, b)) + twirled_circuit = generate_pauli_twirl_variants( + circuit, num_circuits=1, noise_name=noise_name, p=p + )[0] + + for i, current_moment in enumerate(twirled_circuit): + for op in current_moment: + if op.gate in [cirq.CNOT, cirq.CZ]: + for next_op in twirled_circuit[i + 1]: + assert next_op.gate == CIRQ_NOISE_OP[noise_name](p=p) + + +@pytest.mark.parametrize("noise_name", ["bit-flip", "depolarize"]) +def test_noisy_pennylane(noise_name): + p = 0.01 + ops = [ + qml.Hadamard(0), + qml.CNOT((0, 1)), + qml.CZ((0, 1)), + ] + circuit = QuantumTape(ops) + twirled_circuit = generate_pauli_twirl_variants( + circuit, num_circuits=1, noise_name=noise_name, p=p + )[0] + + noisy_gates = ["CNOT", "CZ"] + + for i, op in enumerate(twirled_circuit.operations): + if op.name in noisy_gates: + for j in range(1, len(op.wires) + 1): + assert ( + type(twirled_circuit.operations[i + j]) + == PENNYLANE_NOISE_OP[noise_name] + ) diff --git a/mitiq/rem/inverse_confusion_matrix.py b/mitiq/rem/inverse_confusion_matrix.py index ac52910fd1..96e7271558 100644 --- a/mitiq/rem/inverse_confusion_matrix.py +++ b/mitiq/rem/inverse_confusion_matrix.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. from functools import reduce -from typing import List, Sequence +from typing import Sequence import numpy as np import numpy.typing as npt @@ -14,38 +14,35 @@ def sample_probability_vector( - probability_vector: npt.NDArray[np.float64], samples: int -) -> List[Bitstring]: + probability_vector: Sequence[float], samples: int +) -> list[str]: """Generate a number of samples from a probability distribution as bitstrings. Args: probability_vector: A probability vector. + samples: The number of samples to generate. Returns: A list of sampled bitstrings. + + Example: + >>> sample_probability_vector([0, 1/2, 1/4, 1/4], 4) + ['01', '10', '11', '11'] """ - # sample using the probability distribution given num_values = len(probability_vector) - choices = np.random.choice(num_values, size=samples, p=probability_vector) - - # convert samples to binary strings - bit_width = int(np.log2(num_values)) - binary_repr_vec = np.vectorize(np.binary_repr) - binary_strings = binary_repr_vec(choices, width=bit_width) - - # split the binary strings into an array of ints - bitstrings = ( - np.apply_along_axis( # type: ignore - func1d=np.fromstring, # type: ignore - axis=1, - arr=binary_strings[:, None], - dtype="U1", # type: ignore + if not np.log2(num_values).is_integer(): + raise ValueError( + "The length of the probability vector must be a power of 2." ) - .astype(np.uint8) - .tolist() + + sampled_indices = np.random.choice( + num_values, size=samples, p=probability_vector ) + bit_width = int(np.log2(num_values)) + bitstrings = [format(index, f"0{bit_width}b") for index in sampled_indices] + return bitstrings @@ -60,7 +57,7 @@ def bitstrings_to_probability_vector( bitstrings: All measured bitstrings. Returns: - A probabiity vector corresponding to the measured bitstrings. + A probability vector corresponding to the measured bitstrings. """ pv = np.zeros(2 ** len(bitstrings[0])) for bs in bitstrings: @@ -100,7 +97,7 @@ def generate_inverse_confusion_matrix( def generate_tensored_inverse_confusion_matrix( - num_qubits: int, confusion_matrices: List[npt.NDArray[np.float64]] + num_qubits: int, confusion_matrices: list[npt.NDArray[np.float64]] ) -> npt.NDArray[np.float64]: """ Generates the inverse confusion matrix utilizing the supplied @@ -132,7 +129,7 @@ def generate_tensored_inverse_confusion_matrix( def closest_positive_distribution( quasi_probabilities: npt.NDArray[np.float64], -) -> npt.NDArray[np.float64]: +) -> list[float]: """Given the input quasi-probability distribution returns the closest positive probability distribution (with respect to the total variation distance). @@ -163,7 +160,7 @@ def distance(probabilities: npt.NDArray[np.float64]) -> np.float64: raise ValueError( "REM failed to determine the closest positive distribution." ) - return result.x + return result.x.tolist() def mitigate_measurements( diff --git a/mitiq/rem/tests/test_inverse_confusion_matrix.py b/mitiq/rem/tests/test_inverse_confusion_matrix.py index bd3371c468..7e982469f6 100644 --- a/mitiq/rem/tests/test_inverse_confusion_matrix.py +++ b/mitiq/rem/tests/test_inverse_confusion_matrix.py @@ -22,29 +22,35 @@ ) +def test_sample_probability_vector_invalid_size(): + with pytest.raises(ValueError, match="power of 2"): + sample_probability_vector([1 / 3, 1 / 3, 1 / 3], 3) + + def test_sample_probability_vector_single_qubit(): bitstrings = sample_probability_vector(np.array([1, 0]), 10) - assert all([b == [0] for b in bitstrings]) + assert all(b == "0" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 1]), 10) - assert all([b == [1] for b in bitstrings]) + assert all(b == "1" for b in bitstrings) + np.random.seed(0) bitstrings = sample_probability_vector(np.array([0.5, 0.5]), 1000) - assert isclose(sum([b[0] for b in bitstrings]), 500, rel_tol=0.1) + assert sum(int(b) for b in bitstrings) == 483 def test_sample_probability_vector_two_qubits(): bitstrings = sample_probability_vector(np.array([1, 0, 0, 0]), 10) - assert all([b == [0, 0] for b in bitstrings]) + assert all(b == "00" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 1, 0, 0]), 10) - assert all([b == [0, 1] for b in bitstrings]) + assert all(b == "01" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 0, 1, 0]), 10) - assert all([b == [1, 0] for b in bitstrings]) + assert all(b == "10" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 0, 0, 1]), 10) - assert all([b == [1, 1] for b in bitstrings]) + assert all(b == "11" for b in bitstrings) def test_bitstrings_to_probability_vector(): @@ -64,20 +70,20 @@ def test_bitstrings_to_probability_vector(): assert (pv == np.array([0, 0, 0, 1])).all() -def test_probability_vector_roundtrip(): - for _ in range(10): - pv = np.random.rand(4) - pv /= np.sum(pv) - assert isclose( - np.linalg.norm( - pv - - bitstrings_to_probability_vector( - sample_probability_vector(pv, 1000) - ) - ), - 0, - abs_tol=0.1, - ) +@pytest.mark.parametrize("_", range(10)) +def test_probability_vector_roundtrip(_): + pv = np.random.rand(4) + pv /= np.sum(pv) + assert isclose( + np.linalg.norm( + pv + - bitstrings_to_probability_vector( + sample_probability_vector(pv, 1000) + ) + ), + 0, + abs_tol=0.1, + ) def test_generate_inverse_confusion_matrix(): @@ -137,12 +143,12 @@ def test_generate_tensored_inverse_confusion_matrix( num_qubits, confusion_matrices ) else: - assert np.isclose( + assert np.allclose( generate_tensored_inverse_confusion_matrix( num_qubits, confusion_matrices ), expected, - ).all() + ) def test_mitigate_measurements(): diff --git a/mitiq/tests/test_about.py b/mitiq/tests/test_about.py new file mode 100644 index 0000000000..0ee7b18d7c --- /dev/null +++ b/mitiq/tests/test_about.py @@ -0,0 +1,16 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Tests for mitiq.about().""" + +import mitiq + + +def test_result_and_stdout(capsys): + mitiq.about() + captured = capsys.readouterr() + assert captured.out.startswith( + "\nMitiq: A Python toolkit for implementing" + ) diff --git a/mitiq/tests/test_measurement_result.py b/mitiq/tests/test_measurement_result.py index 676187cc29..925ef16a4f 100644 --- a/mitiq/tests/test_measurement_result.py +++ b/mitiq/tests/test_measurement_result.py @@ -38,6 +38,11 @@ def test_measurement_result_not_bits(): MeasurementResult(result=[[0, 0], [0, 1], [-1, 0]]) +def test_measurement_result_invoked_with_dict(): + with pytest.raises(TypeError, match="from_counts"): + MeasurementResult({"001": 123, "010": 456}) + + def test_filter_qubits(): result = MeasurementResult([[0, 0, 1], [0, 1, 0], [1, 0, 0]]) assert np.allclose(result.filter_qubits([0]), np.array([[0], [0], [1]])) diff --git a/mitiq/tests/test_typing.py b/mitiq/tests/test_typing.py index 58f15b1982..fd6b25dd0a 100644 --- a/mitiq/tests/test_typing.py +++ b/mitiq/tests/test_typing.py @@ -1,3 +1,8 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + import os from mitiq.typing import SUPPORTED_PROGRAM_TYPES diff --git a/mitiq/typing.py b/mitiq/typing.py index 8fddbe4632..37e9e05842 100644 --- a/mitiq/typing.py +++ b/mitiq/typing.py @@ -112,7 +112,12 @@ class MeasurementResult: ``tuple(range(self.nqubits))``, where ``self.nqubits`` is the bitstring length deduced from ``result``. - Note: + Example: + >>> mr = MeasurementResult(["001", "010", "001"]) + >>> mr.get_counts() + {'001': 2, '010': 1} + + Warning: Use caution when selecting the default option for ``qubit_indices``, especially when estimating an :class:`.Observable` acting on a subset of qubits. In this case Mitiq @@ -125,6 +130,11 @@ class MeasurementResult: def __post_init__(self) -> None: # Validate arguments + if isinstance(self.result, dict): + raise TypeError( + "Use the MeasurementResult.from_counts method to instantiate " + "a MeasurementResult object from a dictionary." + ) symbols = set(b for bits in self.result for b in bits) if not (symbols.issubset({0, 1}) or symbols.issubset({"0", "1"})): raise ValueError("Bitstrings should look like '011' or [0, 1, 1].") diff --git a/mitiq/zne/inference.py b/mitiq/zne/inference.py index 87ebb1712c..409b08acf7 100644 --- a/mitiq/zne/inference.py +++ b/mitiq/zne/inference.py @@ -420,6 +420,19 @@ class BatchedFactory(Factory, ABC): Specific (non-adaptive) extrapolation algorithms are derived from this class by defining the `reduce` method. + + Args: + scale_factors: Sequence of noise scale factors at which expectation + values should be measured. + shot_list: Optional sequence of integers corresponding to the + number of samples taken for each expectation value. If this + argument is explicitly passed to the factory, it must have the + same length of scale_factors and the executor function must + accept "shots" as a valid keyword argument. + + Raises: + ValueError: If the number of scale factors is less than 2. + TypeError: If shot_list is provided and has any non-integer values. """ def __init__( @@ -427,21 +440,6 @@ def __init__( scale_factors: Sequence[float], shot_list: Optional[List[int]] = None, ) -> None: - """Constructs a BatchedFactory. - - Args: - scale_factors: Sequence of noise scale factors at which expectation - values should be measured. - shot_list: Optional sequence of integers corresponding to the - number of samples taken for each expectation value. If this - argument is explicitly passed to the factory, it must have the - same length of scale_factors and the executor function must - accept "shots" as a valid keyword argument. - - Raises: - ValueError: If the number of scale factors is less than 2. - TypeError: If shot_list is provided and has any non-integer values. - """ if len(scale_factors) < 2: raise ValueError("At least 2 scale factors are necessary.") @@ -805,7 +803,6 @@ def __init__( order: int, shot_list: Optional[List[int]] = None, ) -> None: - """Instantiates a new object of this Factory class.""" if order > len(scale_factors) - 1: raise ValueError( "The extrapolation order cannot exceed len(scale_factors) - 1." @@ -1127,7 +1124,6 @@ def __init__( avoid_log: bool = False, shot_list: Optional[List[int]] = None, ) -> None: - """Instantiate an new object of this Factory class.""" super(ExpFactory, self).__init__(scale_factors, shot_list) if not (asymptote is None or isinstance(asymptote, float)): raise ValueError( @@ -1247,7 +1243,6 @@ def __init__( avoid_log: bool = False, shot_list: Optional[List[int]] = None, ) -> None: - """Instantiates a new object of this Factory class.""" super(PolyExpFactory, self).__init__(scale_factors, shot_list) if not (asymptote is None or isinstance(asymptote, float)): raise ValueError( @@ -1519,7 +1514,6 @@ def __init__( avoid_log: bool = False, max_scale_factor: float = 6.0, ) -> None: - """Instantiate a new object of this Factory class.""" super(AdaExpFactory, self).__init__() if not (asymptote is None or isinstance(asymptote, float)): raise ValueError( diff --git a/mitiq/zne/scaling/layer_scaling.py b/mitiq/zne/scaling/layer_scaling.py index 24b47dd481..89f2c2c1eb 100644 --- a/mitiq/zne/scaling/layer_scaling.py +++ b/mitiq/zne/scaling/layer_scaling.py @@ -24,6 +24,23 @@ def layer_folding( ) -> cirq.Circuit: """Applies a variable amount of folding to select layers of a circuit. + Note that this method only works for the univariate extrapolation methods. + It allows a user to choose which layers in the input circuit will be + scaled. + + .. seealso:: + + If you would prefer to + use a multivariate extrapolation method for unitary + folding, use + :func:`mitiq.lre.multivariate_scaling.layerwise_folding` instead. + + The layerwise folding required for multivariate extrapolation is + different as the layers in the input circuit have to be scaled in + a specific pattern. The required specific pattern for multivariate + extrapolation does not allow a user to provide a choice of which + layers to fold. + Args: circuit: The input circuit. layers_to_fold: A list with the index referring to the layer number, diff --git a/mitiq/zne/scaling/tests/test_layer_scaling.py b/mitiq/zne/scaling/tests/test_layer_scaling.py index 1208eb5523..bb84f2baad 100644 --- a/mitiq/zne/scaling/tests/test_layer_scaling.py +++ b/mitiq/zne/scaling/tests/test_layer_scaling.py @@ -5,9 +5,16 @@ """Unit tests for scaling by layer.""" +import random +from itertools import product +from unittest.mock import patch + +import pytest from cirq import Circuit, LineQubit, ops -from mitiq.zne.scaling import layer_folding +from mitiq import SUPPORTED_PROGRAM_TYPES +from mitiq.interface import convert_from_mitiq, convert_to_mitiq +from mitiq.zne.scaling import get_layer_folding, layer_folding def test_layer_folding_with_measurements(): @@ -53,24 +60,59 @@ def test_layer_folding(): # Iterate over every possible combination of layerwise folds for a maximum # number of 5-folds. total_folds = 5 - for i1 in range(total_folds): - for i2 in range(total_folds): - for i3 in range(total_folds): - layers_to_fold = [i1, i2, i3] - - folded_circuit = layer_folding(circ, layers_to_fold) - - # For a given layer, the number of copies on a layer will be - # 2n + 1 where "n" is the number of folds to perform. - qreg = LineQubit.range(3) - correct = Circuit( - # Layer-1 - [ops.H.on_each(*qreg)] * (2 * (layers_to_fold[0]) + 1), - # Layer-2 - [ops.CNOT.on(qreg[0], qreg[1])] - * (2 * (layers_to_fold[1]) + 1), - [ops.X.on(qreg[2])] * (2 * (layers_to_fold[1]) + 1), - # Layer-3 - [ops.TOFFOLI.on(*qreg)] * (2 * (layers_to_fold[2]) + 1), - ) - assert folded_circuit == correct + for i1, i2, i3 in product(range(total_folds), repeat=3): + folded_circuit = layer_folding(circ, [i1, i2, i3]) + + # For a given layer, the number of copies on a layer will be + # 2n + 1 where "n" is the number of folds to perform. + a, b, c = LineQubit.range(3) + correct = Circuit( + # Layer-1 + [ops.H.on_each(*(a, b, c))] * (2 * i1 + 1), + # Layer-2 + [ops.CNOT.on(a, b)] * (2 * i2 + 1), + [ops.X.on(c)] * (2 * i2 + 1), + # Layer-3 + [ops.TOFFOLI.on(*(a, b, c))] * (2 * i3 + 1), + ) + assert folded_circuit == correct + + +@pytest.mark.parametrize("circuit_type", SUPPORTED_PROGRAM_TYPES.keys()) +def test_layer_folding_all_qprograms(circuit_type): + """This test only ensures proper depth of layer-folded non-cirq circuits + as the mitiq conversion functions alter structure/gate composition.""" + qreg = LineQubit.range(3) + circuit = Circuit( + [ops.H.on_each(*qreg)], + [ops.CNOT.on(qreg[0], qreg[1])], + [ops.X.on(qreg[2])], + [ops.TOFFOLI.on(*qreg)], + ) + num_layers = len(circuit) + circuit = convert_from_mitiq(circuit, circuit_type) + layers_to_fold = random.choices(range(5), k=num_layers) + folded_circuit = layer_folding(circuit, layers_to_fold) + folded_mitiq_circuit = convert_to_mitiq(folded_circuit)[0] + num_ideal_layers = sum(2 * n + 1 for n in layers_to_fold) + if circuit_type == "pyquil": + # this block is needed for pyquil because of some quirks that pop up + # when converting to and from pyquil that does not make exact equality. + assert len(folded_mitiq_circuit) >= num_ideal_layers + else: + assert len(folded_mitiq_circuit) == num_ideal_layers + + +@patch("mitiq.zne.scaling.layer_scaling.layer_folding") +def test_get_layer_folding(mock_layer_folding): + a, b = LineQubit.range(2) + circuit = Circuit(ops.X(a), ops.CNOT(a, b), ops.Y(b)) + layer_index = 1 + scale_factor = 3 + + folding_func = get_layer_folding(layer_index) + folding_func(circuit, scale_factor) + + mock_layer_folding.assert_called_once_with( + circuit, layers_to_fold=[0, 1, 0] + ) diff --git a/mitiq/zne/tests/test_zne.py b/mitiq/zne/tests/test_zne.py index 3689572a3d..ad528e1562 100644 --- a/mitiq/zne/tests/test_zne.py +++ b/mitiq/zne/tests/test_zne.py @@ -6,24 +6,32 @@ """Unit tests for zero-noise extrapolation.""" import functools +import random from typing import List +from unittest.mock import Mock import cirq import numpy as np import pytest import qiskit +import qiskit.circuit from qiskit_aer import AerSimulator from mitiq import QPROGRAM, SUPPORTED_PROGRAM_TYPES from mitiq.benchmarks.randomized_benchmarking import generate_rb_circuits from mitiq.interface import accept_any_qprogram_as_input, convert_from_mitiq +from mitiq.interface.mitiq_braket import to_braket from mitiq.interface.mitiq_cirq import ( compute_density_matrix, sample_bitstrings, ) +from mitiq.interface.mitiq_pennylane import to_pennylane +from mitiq.interface.mitiq_pyquil import to_pyquil +from mitiq.interface.mitiq_qibo import to_qibo from mitiq.interface.mitiq_qiskit import ( execute_with_shots_and_noise, initialized_depolarizing_noise, + to_qiskit, ) from mitiq.observable import Observable, PauliString from mitiq.zne import ( @@ -40,10 +48,13 @@ RichardsonFactory, ) from mitiq.zne.scaling import ( + fold_all, fold_gates_at_random, + fold_global, get_layer_folding, insert_id_layers, ) +from mitiq.zne.zne import combine_results, scaled_circuits BASE_NOISE = 0.007 TEST_DEPTH = 30 @@ -484,32 +495,34 @@ def test_execute_with_zne_with_supported_circuits(circuit_type): assert abs(unmitigated - expected) > abs(zne_value - expected) -@pytest.mark.parametrize("circuit_type", SUPPORTED_PROGRAM_TYPES.keys()) -def test_layerwise_execute_with_zne_with_supported_circuits(circuit_type): +def test_layerwise_folding_with_zne(): # Define a circuit equivalent to the identity qreg = cirq.LineQubit.range(2) - cirq_circuit = cirq.Circuit( + circuit = cirq.Circuit( cirq.H.on_each(qreg), cirq.CNOT(*qreg), cirq.CNOT(*qreg), cirq.H.on_each(qreg), ) - # Convert to one of the supported program types - circuit = convert_from_mitiq(cirq_circuit, circuit_type) - expected = generic_executor(circuit, noise_level=0.0) - unmitigated = generic_executor(circuit) - # Use odd scale factors for deterministic results - fac = RichardsonFactory([1, 3, 5]) - # Layerwise-fold + circuit_depth = len(circuit) + mock_executor = Mock(side_effect=lambda _: random.random()) layer_to_fold = 0 fold_layer_func = get_layer_folding(layer_to_fold) + scale_factors = [1, 3, 5] + factory = RichardsonFactory(scale_factors) - zne_value = execute_with_zne( - circuit, generic_executor, factory=fac, scale_noise=fold_layer_func + execute_with_zne( + circuit, mock_executor, factory=factory, scale_noise=fold_layer_func ) - - # Test zero noise limit is better than unmitigated expectation value - assert abs(unmitigated - expected) > abs(zne_value - expected) + assert mock_executor.call_count == len(scale_factors) + circuit_depths = [ + len(args[0]) for args, kwargs in mock_executor.call_args_list + ] + assert circuit_depths == [ + circuit_depth, + circuit_depth + 2, + circuit_depth + 4, + ] def test_execute_with_zne_transpiled_qiskit_circuit(): @@ -540,3 +553,78 @@ def execute(circuit: qiskit.QuantumCircuit, shots: int = 8192) -> float: # Note: Unmitigated value is also (usually) within 10% of the true value. # This is more to test usage than effectiveness. assert abs(zne_value - true_value) < 0.1 + + +def test_execute_zne_on_qiskit_circuit_with_QFT(): + """Tests ZNE of a Qiskit device with a QFT gate.""" + + def qs_noisy_simulation( + circuit: qiskit.QuantumCircuit, shots: int = 1 + ) -> float: + noise_model = initialized_depolarizing_noise(noise_level=0.02) + backend = AerSimulator(noise_model=noise_model) + job = backend.run(circuit.decompose(), shots=shots) + return job.result().get_counts().get("0", 0.0) / shots + + circuit = qiskit.QuantumCircuit(1) + circuit &= qiskit.circuit.library.QFT(1) + circuit.measure_all() + + mitigated = execute_with_zne(circuit, qs_noisy_simulation) + assert abs(mitigated) < 1000 + + +@pytest.mark.parametrize( + "noise_scaling_method", + [fold_gates_at_random, insert_id_layers, fold_global, fold_all], +) +@pytest.mark.parametrize( + "extrapolation_factory", [RichardsonFactory, LinearFactory] +) +@pytest.mark.parametrize( + "to_frontend", + [None, to_qiskit, to_braket, to_pennylane, to_pyquil, to_qibo], +) +def test_two_stage_zne( + noise_scaling_method, extrapolation_factory, to_frontend +): + qreg = cirq.LineQubit.range(2) + cirq_circuit = cirq.Circuit( + cirq.H.on_each(qreg), + cirq.CNOT(*qreg), + cirq.CNOT(*qreg), + cirq.H.on_each(qreg), + ) + if to_frontend is not None: + frontend_circuit = to_frontend(cirq_circuit) + else: + frontend_circuit = cirq_circuit + + scale_factors = [1, 3, 5] + circs = scaled_circuits( + frontend_circuit, scale_factors, noise_scaling_method + ) + + assert len(circs) == len(scale_factors) + + np.random.seed(42) + + def executor(circuit): + return np.random.random() + + results = [executor(cirq_circuit) for _ in range(3)] + extrapolation_method = extrapolation_factory.extrapolate + two_stage_zne_res = combine_results( + scale_factors, results, extrapolation_method + ) + + assert isinstance(two_stage_zne_res, float) + + np.random.seed(42) + zne_res = execute_with_zne( + cirq_circuit, + executor, + factory=extrapolation_factory(scale_factors), + scale_noise=noise_scaling_method, + ) + assert np.isclose(zne_res, two_stage_zne_res) diff --git a/mitiq/zne/zne.py b/mitiq/zne/zne.py index c48c431ec1..b230137488 100644 --- a/mitiq/zne/zne.py +++ b/mitiq/zne/zne.py @@ -6,13 +6,61 @@ """High-level zero-noise extrapolation tools.""" from functools import wraps -from typing import Callable, List, Optional, Union +from typing import Callable, List, Optional, Sequence, Union from mitiq import QPROGRAM, Executor, Observable, QuantumResult from mitiq.zne.inference import Factory, RichardsonFactory from mitiq.zne.scaling import fold_gates_at_random +def scaled_circuits( + circuit: QPROGRAM, + scale_factors: list[float], + scale_method: Callable[[QPROGRAM, float], QPROGRAM], +) -> list[QPROGRAM]: + """Given a circuit, scale_factors and a scale_method, outputs a list + of circuits that will be used in ZNE. + + Args: + circuit: The input circuit to execute with ZNE. + scale_factors: An array of noise scale factors. + scale_method: The function for scaling the noise of a quantum circuit. + A list of built-in functions can be found in ``mitiq.zne.scaling``. + + Returns: + The scaled circuits using the scale_method. + """ + circuits = [] + for scale_factor in scale_factors: + circuits.append(scale_method(circuit, scale_factor)) + + return circuits + + +def combine_results( + scale_factors: Sequence[float], + results: Sequence[float], + extrapolation_method: Callable[[Sequence[float], Sequence[float]], float], +) -> float: + """Computes the error-mitigated expectation value associated to the + input results from executing the scaled circuits, via the application + of zero-noise extrapolation (ZNE). + + Args: + scale_factors: An array of noise scale factors. + results: An array storing the results of running the scaled circuits. + extrapolation_method: The function for scaling the noise of a + quantum circuit. A list of built-in functions can be found + in ``mitiq.zne.scaling``. + + Returns: + The expectation value estimated with ZNE. + """ + res = extrapolation_method(scale_factors, results) + + return res + + def execute_with_zne( circuit: QPROGRAM, executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]], diff --git a/pyproject.toml b/pyproject.toml index 4c79f8e840..8e93f06baa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,7 @@ [tool.ruff] exclude = ["__init__.py"] line-length = 79 +target-version = 'py312' [tool.ruff.lint] select = [ diff --git a/requirements/requirements-braket.txt b/requirements/requirements-braket.txt index 2f954b8cc6..b9d45d00dd 100644 --- a/requirements/requirements-braket.txt +++ b/requirements/requirements-braket.txt @@ -1,2 +1,2 @@ amazon-braket-sdk~=1.69.0 -cirq-ionq>=1.0.0,<1.4.0 \ No newline at end of file +cirq-ionq>=1.4.0,<1.5.0 \ No newline at end of file diff --git a/requirements/requirements-cirq.txt b/requirements/requirements-cirq.txt index 8f9e15b617..15e5ef12ba 100644 --- a/requirements/requirements-cirq.txt +++ b/requirements/requirements-cirq.txt @@ -1,4 +1,4 @@ # This file is redundant, since Cirq is the core internal representation # for quantum circuits in Mitiq. # Keeping this file anyway for consistency with other integrations. -cirq-core>=1.0.0,<1.4.0 +cirq-core>=1.4.0,<1.5.0 diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 5b6968b7f6..798ec58460 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -7,22 +7,25 @@ mypy==1.0.0 types-tabulate # Documentation and examples. -Sphinx==7.2.6 +Sphinx==8.0.2 sphinxcontrib-bibtex==2.6.2 sphinx-copybutton==0.5.2 sphinx-autodoc-typehints==2.0.0 -myst-nb==1.0.0 -myst-parser==3.0.1 -pydata-sphinx-theme==0.15.2 +sphinx-design==0.6.1 +sphinx-tags==0.4 +myst-nb==1.1.1 +myst-parser==4.0.0 +pydata-sphinx-theme==0.15.4 jupytext==1.16.1 sphinx-gallery==0.15.0 nbsphinx==0.9.3 matplotlib==3.8.1 pandas==2.1.3 -pyscf==2.6.0; sys_platform != 'win32' +pyscf==2.7.0; sys_platform != 'win32' openfermion==1.6.1; sys_platform != 'win32' openfermionpyscf==0.5; sys_platform != 'win32' bqskit==1.1.1 seaborn==0.13.0 stim==1.13.0 stimcirq==1.13.0 +pyqrack==1.31.1 \ No newline at end of file diff --git a/requirements/requirements-pennylane.txt b/requirements/requirements-pennylane.txt index e43860ee2d..e42f642706 100644 --- a/requirements/requirements-pennylane.txt +++ b/requirements/requirements-pennylane.txt @@ -1,4 +1,2 @@ pennylane-qiskit~=0.36.0 -pennylane~=0.36.0 -# TODO: Remove qiskit-ibm-provider when upgrading qiskit-ibm-runtime in requirements-qiskit -qiskit-ibm-provider~=0.11.0; python_version == '3.9' \ No newline at end of file +pennylane~=0.36.0 \ No newline at end of file diff --git a/requirements/requirements-pyquil.txt b/requirements/requirements-pyquil.txt index 7c8f5861f7..9e0041b3ed 100644 --- a/requirements/requirements-pyquil.txt +++ b/requirements/requirements-pyquil.txt @@ -1,2 +1,2 @@ pyquil~=3.5.4 -cirq-rigetti>=1.0.0,<1.4.0 \ No newline at end of file +cirq-rigetti>=1.4.0,<1.5.0 \ No newline at end of file diff --git a/requirements/requirements-qibo.txt b/requirements/requirements-qibo.txt index ee8ec25038..704b3e8e80 100644 --- a/requirements/requirements-qibo.txt +++ b/requirements/requirements-qibo.txt @@ -1 +1 @@ -qibo==0.2.8 # TODO: unpin this +qibo==0.2.12 # TODO: unpin this diff --git a/requirements/requirements-qiskit.txt b/requirements/requirements-qiskit.txt index 6f21b26045..0894296d73 100644 --- a/requirements/requirements-qiskit.txt +++ b/requirements/requirements-qiskit.txt @@ -1,4 +1,4 @@ -qiskit~=1.1.0 -qiskit-aer~=0.14.2 +qiskit~=1.2.2 +qiskit-aer~=0.15.1 qiskit-ibm-runtime~=0.20.0 ply==3.11 \ No newline at end of file diff --git a/requirements/requirements.txt b/requirements/requirements.txt index bced4585e5..d1fd7d8ca9 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,4 @@ numpy>=1.22.0 -scipy>=1.10.1,<=1.13.1 -cirq-core>=1.0.0,<1.4.0 +scipy>=1.10.1,<=1.14.1 +cirq-core>=1.4.0,<1.5.0 tabulate diff --git a/setup.py b/setup.py index 2d500d2270..db01d4f775 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ def load_requirements(filename): "Documentation": "https://mitiq.readthedocs.io/en/stable/", "Source": "https://github.com/unitaryfund/mitiq/", }, - python_requires=">=3.9,<3.12", + python_requires=">=3.10,<3.13", ) # restore _version.py to its previous state